对于在Django中使用modeltranslation翻译的字段,如何在upload_to方法中获取FileField的字段名?

Posted

技术标签:

【中文标题】对于在Django中使用modeltranslation翻译的字段,如何在upload_to方法中获取FileField的字段名?【英文标题】:How to get the fieldname of a FileField in the upload_to method, for a field translated with modeltranslation in Django? 【发布时间】:2020-05-09 22:13:19 【问题描述】:

我在 FileField 上使用 django 模型翻译。

我希望将此文件上传到路径/path/to/file/<lang>/file.ext,我想最好的方法是从upload_to 正在运行的字段名(file_en、file_it、file_fr、...)中提取语言。

# 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", )

【问题讨论】:

【参考方案1】:

尝试使用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"

【讨论】:

我不能,因为 get_language() 正在使用当前用户的语言。想象一下,我是一名使用意大利语的管理员,我想上传一个法语文件。在 upload_to 评估中,get_language() 将返回“it”【参考方案2】:

这样的事情应该可以工作。

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 设置中定义了enfr

如果传递给 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 下

【讨论】:

我认为你应该向 django modeltranslation 老兄提出拉取请求!

以上是关于对于在Django中使用modeltranslation翻译的字段,如何在upload_to方法中获取FileField的字段名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 中将虚拟数据添加到数据表中?

对于 django 模型,如何获取 django 管理 URL 以添加另一个或列出对象等?

在 django-crispy 按钮名称中使用 django 模板变量

Django源码分析视频预售中

Django中对于上传的文件的保存方法(时间+文件名)

django 对于mysql的常用操作(使用orm)