在Django管理员中为每个模型添加自定义字段

时间:2019-09-08 00:14:36

标签: python django django-models django-admin

我正在我的个人网站上工作,想同时处理前端和数据库翻译(即使用2-3种语言进行翻译)。尽管已经有很多第三方应用程序可用于处理数据库中的翻译,但我认为构建自己的应用程序既有趣又有趣。


我在最后一步。到目前为止,一切都很好。快速回顾一下它的工作原理(跳过几件事):

  • 我的django项目中有一个“翻译”应用程序
  • 它提供了一些不同的表,最值得注意的是:
    • 语言:可用语言列表
    • 项目:相当于说“这是'Job'模型的实例33的'name'字段”
    • 翻译:语言和项目之间的多对多。基本上是给定项目的所有翻译。
  • 此应用提供了一个名为“翻译模型”的抽象模型
  • 常规应用中具有待翻译字段的每个模型都必须继承此模型
  • 通过信号与FK的关系,该应用程序然后(自动)处理Item和Translation表中条目的创建和删除

好吧,假设我们有一个“工作”模型,其中有两个必须翻译的字段:标题描述。现在,每当我在Job中创建新条目时,就会自动发生以下情况:

  • 在Item中创建2个新条目:
    • 职位编号,名称
    • 职位编号,描述
  • 在Translation中创建4个条目:
    • 职位编号,名称,法语
    • 职位编号,描述,法语
    • 职位编号,姓名,英语
    • 职位nX,描述英语

我的问题如下:

  • 我有很多表格,例如“工作”,它们会在Item中触发新条目,然后触发Translation
  • 这些表都具有指向Item表的ForeignKey,但是Item没有这些表的ForeignKey(因为同一列存储来自不同表的数据)

我想做的是:

  • 在管理员上,当我进入“ Job”之类的表格时,我想查看并直接更新其翻译文本(Translation.text)。这意味着看到上面提到的其4个不同的“翻译”条目。我已经有一个获取 Translation 实例的函数,现在在管理员中显示和编辑它们是一个更大的问题。
  • 有没有一种方法可以全局应用此更改,而不是手动覆盖每个模型/管理员表单。

我的想法是“通过添加一个专用于翻译的新区域来覆盖常规的 change_form.html ,该区域将获取与某个实例相关的所有Translation条目”(仅当该实例受制于翻译)。但不确定如何做到这一点。

(请注意,我已经自动检测到所有需要翻译的模型,并且可以轻松获取它们的特定字段)

任何帮助将不胜感激:)

1 个答案:

答案 0 :(得分:0)

对于偶然发现此主题的任何人,我都设法解决了这个问题,并发布了自己的数据库翻译应用程序。该代码是完全开源的,可以在这里找到:https://pypi.org/project/django-database-translation/

对于我的原始帖子中提到的问题,我设法应用了以下逻辑:

  • 我创建了一个ModelForm,可以动态生成项目的“要翻译的字段”
  • 我创建了一个使用此表单的ModelAdmin,并生成与上述表单相同的字段名称
  • 因此,如果我在管理员中继续执行“工作”项,则可以访问其所有翻译,并可以直接在此页面上更改它们。

以下是摘录,也可以在https://github.com/Jordan-Kowal/django_database_translation

上找到
class DynamicTranslationForm(forms.ModelForm):
    """
    Form to use in a ModelAdmin for any model that has fields to translate.
    It will allow you to display and edit the Translation instances linked to the object.
    Since fields are dynamically generated, you must override the get_fieldsets method in the admin (or else they won't show)
    The "TranslatedAdmin" ModelAdmin natively use this form.
    """

    # ----------------------------------------
    # Core Methods
    # ----------------------------------------
    def __init__(self, *args, **kwargs):
        """Overridden method to dynamically add a new field for each Translation linked with our object"""
        super(DynamicTranslationForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.set_translation_info()
            for translation in self.translations:
                self.fields[translation["fieldname"]] = translation["field"]
                self.initial[translation["fieldname"]] = translation["instance"].text

    def save(self, commit=True):
        """Overridden method to save the updated Translation texts"""
        if self.instance.pk:
            for translation in self.translations:
                obj = translation["instance"]
                fieldname = translation["fieldname"]
                value = self.cleaned_data[fieldname]
                obj.text = value
                obj.save()
        return super(DynamicTranslationForm, self).save(commit=commit)

    # ----------------------------------------
    # Custom Methods
    # ----------------------------------------
    def set_translation_info(self):
        """
        Finds all the Translation instances linked to our object, and stores their info in an attribute
        The attribute is a list of dict, each dict containing the information of one translation
        """
        obj = self.instance
        information = []
        translations = obj.get_translations()
        for translation in translations:
            fieldname = create_translation_fieldname(translation)
            information.append({
                "instance": translation,
                "fieldname": fieldname,
                "field": forms.CharField(required=False, widget=forms.Textarea)
            })
        self.translations = information
# ADMIN.PY
class TranslatedAdmin(admin.ModelAdmin):
    """
    ModelAdmin to use as parent for any model that has fields to translate
    It comes with the "DynamicTranslationForm" and custom methods to display its fields
    """

    # ----------------------------------------
    # Config
    # ----------------------------------------
    form = DynamicTranslationForm

    # ----------------------------------------
    # Detail View
    # ----------------------------------------
    fieldsets = []

    # ----------------------------------------
    # Custom Methods
    # ----------------------------------------
    def get_form(self, request, obj=None, **kwargs):
        """Required for get_fieldsets"""
        kwargs['fields'] = flatten_fieldsets(self.fieldsets)
        return super().get_form(request, obj, **kwargs)

    def get_fieldsets(self, request, obj=None):
        """
        Allows us to display the field dynamically created by "DynamicTranslationForm"
        The fieldnames in "DynamicTranslationForm" and this function must be identical
        In other words:
        - "DynamicTranslationForm" creates the fields
        - This function adds fields with the same name to the fieldsets
        - As a result, the fields now appear on the form
        """
        fieldsets = self.fieldsets.copy()
        # Get current Item
        url = request.build_absolute_uri("?")
        if url.endswith("/change/"):
            url = url[:-8]
            object_id = url.split("/")[-1]
            obj = self.model.objects.get(pk=object_id)
            # Create a field for each translation associated with our object
            fields = []
            translations = obj.get_translations()
            for translation in translations:
                fieldname = create_translation_fieldname(translation)
                fields.append(fieldname)
            # Add a fieldset with our fields
            fieldsets.append(['TRANSLATIONS', {'fields': fields}])
        return fieldsets
相关问题