如何在 upload_to 方法中获取 FileField 的字段名,用于在 Django 中使用 modeltranslation 翻译的字段?
How to get the fieldname of a FileField in the upload_to method, for a field translated with modeltranslation in Django?
我在 FileField 上使用 django modeltranslation。
我希望将此文件上传到路径 /path/to/file/<lang>/file.ext
中,我想最好的方法是从字段名中提取语言 (file_en、file_it、file_fr, ...) 其中 upload_to
正在运行。
# models.py
def upload_method(instance, filename):
lang = "" # how to get this variable?
return f"/path/to/file/{lang}/file.ext"
class Obj(models.Model):
file = models.FileField(upload_to=upload_method)
# translation.py
@register(models.Obj)
class ObjTranslationOptions(TranslationOptions):
fields = ("file", )
尝试使用get_language
方法:
from django.utils.translation import get_language
def upload_method(instance, filename):
lang = get_language()
return f"/path/to/file/{lang}/file.ext"
像这样的东西应该有用。
from modeltranslation.translator import translator
from django.db.models import FileField
import os
class TranslationMeta(type):
def __init__(self, name, bases, attrs):
for attrname, attrvalue in attrs.items():
if self.is_translated_field(name, attrname):
field = attrvalue
if isinstance(field, FileField):
self.update_upload_to(field, attrname)
super().__init__(name, bases, attrs)
def is_translated_field(self, class_name, attr_name):
opts = translator.get_options_for_model(self)
return attr_name in opts.get_field_names()
def update_upload_to(self, field, attr_name):
opts = translator.get_options_for_model(self)
translated_fields = opts.fields[attr_name]
for trans_field in translated_fields:
# print(trans_field.name)
# print(trans_field.language)
trans_field.upload_to = self.custom_upload_to(field.upload_to, trans_field.language)
def custom_upload_to(self, base_upload_to, language):
# If the original upload_to parameter is a callable,
# return a function that calls the original upload_to
# function and inserts the language as the final folder
# in the path
# If the original upload_to function returned /path/to/file.png,
# then the final path will be /path/to/en/file.png for the
# english field
if callable(base_upload_to):
def upload_to(instance, filename):
path = base_upload_to(instance, filename)
return os.path.join(
os.path.dirname(path),
language,
os.path.basename(path))
return upload_to
# If the original upload_to parameter is a path as a string,
# insert the language as the final folder in the path
# /path/to/file.png becomes /path/to/en/file.png for the
# english field
else:
return os.path.join(
os.path.dirname(base_upload_to),
language,
os.path.basename(base_upload_to))
# This is how you would use this class
class MyModel(models.Model, metaclass=TranslationMeta):
field = FileField()
m = MyModel(models.Model)
print(m.field.upload_to)
它使用内省动态覆盖由 django-modeltranslation 在幕后生成的每个特定语言的 FileField 的 upload_to
参数。
以这个模型为例:
class MyModel(models.Model):
field = FileField(upload_to=...)
如果您通过添加
将 field
定义为可翻译字段
from modeltranslation.translator import register, TranslationOptions
from . import models
@register(models.MyModel)
class MyModelTranslationOptions(TranslationOptions):
fields = ("field",)
在translation.py
中,django-modeltranslation 会生成类似
的东西
class MyModel(models.Model):
field = FileField(upload_to=...)
field_en = FileField(upload_to=...)
field_fr = FileField(upload_to=...)
如果您在 LANGUAGES
设置中定义了 en
和 fr
。
如果传递给 FileField 的 upload_to
参数是字符串形式的路径,它会被插入该语言文件夹的相同路径覆盖。
如果它是一个函数,那么该语言的文件夹将插入到该函数返回的路径中。
例如,如果您有
class MyModel(models.Model):
field = FileField(upload_to="/path/to/file.png")
或
def get_upload_path(instance, filename):
return "path/to/file.png"
class MyModel(models.Model):
field = FileField(upload_to=get_upload_path)
那么,在这两种情况下:
- 文件的英文版本将存储在 /path/to/en/file.png
下
- 法语版本的文件将存储在 /path/to/fr/file.png
下
我在 FileField 上使用 django modeltranslation。
我希望将此文件上传到路径 /path/to/file/<lang>/file.ext
中,我想最好的方法是从字段名中提取语言 (file_en、file_it、file_fr, ...) 其中 upload_to
正在运行。
# models.py
def upload_method(instance, filename):
lang = "" # how to get this variable?
return f"/path/to/file/{lang}/file.ext"
class Obj(models.Model):
file = models.FileField(upload_to=upload_method)
# translation.py
@register(models.Obj)
class ObjTranslationOptions(TranslationOptions):
fields = ("file", )
尝试使用get_language
方法:
from django.utils.translation import get_language
def upload_method(instance, filename):
lang = get_language()
return f"/path/to/file/{lang}/file.ext"
像这样的东西应该有用。
from modeltranslation.translator import translator
from django.db.models import FileField
import os
class TranslationMeta(type):
def __init__(self, name, bases, attrs):
for attrname, attrvalue in attrs.items():
if self.is_translated_field(name, attrname):
field = attrvalue
if isinstance(field, FileField):
self.update_upload_to(field, attrname)
super().__init__(name, bases, attrs)
def is_translated_field(self, class_name, attr_name):
opts = translator.get_options_for_model(self)
return attr_name in opts.get_field_names()
def update_upload_to(self, field, attr_name):
opts = translator.get_options_for_model(self)
translated_fields = opts.fields[attr_name]
for trans_field in translated_fields:
# print(trans_field.name)
# print(trans_field.language)
trans_field.upload_to = self.custom_upload_to(field.upload_to, trans_field.language)
def custom_upload_to(self, base_upload_to, language):
# If the original upload_to parameter is a callable,
# return a function that calls the original upload_to
# function and inserts the language as the final folder
# in the path
# If the original upload_to function returned /path/to/file.png,
# then the final path will be /path/to/en/file.png for the
# english field
if callable(base_upload_to):
def upload_to(instance, filename):
path = base_upload_to(instance, filename)
return os.path.join(
os.path.dirname(path),
language,
os.path.basename(path))
return upload_to
# If the original upload_to parameter is a path as a string,
# insert the language as the final folder in the path
# /path/to/file.png becomes /path/to/en/file.png for the
# english field
else:
return os.path.join(
os.path.dirname(base_upload_to),
language,
os.path.basename(base_upload_to))
# This is how you would use this class
class MyModel(models.Model, metaclass=TranslationMeta):
field = FileField()
m = MyModel(models.Model)
print(m.field.upload_to)
它使用内省动态覆盖由 django-modeltranslation 在幕后生成的每个特定语言的 FileField 的 upload_to
参数。
以这个模型为例:
class MyModel(models.Model):
field = FileField(upload_to=...)
如果您通过添加
将field
定义为可翻译字段
from modeltranslation.translator import register, TranslationOptions
from . import models
@register(models.MyModel)
class MyModelTranslationOptions(TranslationOptions):
fields = ("field",)
在translation.py
中,django-modeltranslation 会生成类似
class MyModel(models.Model):
field = FileField(upload_to=...)
field_en = FileField(upload_to=...)
field_fr = FileField(upload_to=...)
如果您在 LANGUAGES
设置中定义了 en
和 fr
。
如果传递给 FileField 的 upload_to
参数是字符串形式的路径,它会被插入该语言文件夹的相同路径覆盖。
如果它是一个函数,那么该语言的文件夹将插入到该函数返回的路径中。
例如,如果您有
class MyModel(models.Model):
field = FileField(upload_to="/path/to/file.png")
或
def get_upload_path(instance, filename):
return "path/to/file.png"
class MyModel(models.Model):
field = FileField(upload_to=get_upload_path)
那么,在这两种情况下:
- 文件的英文版本将存储在 /path/to/en/file.png 下
- 法语版本的文件将存储在 /path/to/fr/file.png 下