如何在模型中没有表达 FK 关系的管理更改页面中的字段上实现 add/edit 链接?

How to implement the add/edit links on a field in the admin change page that does not have an expressed FK relationship in the model?

我有一个 Django 3.x 网站。共有 3 个模型,其中 DocumentMetaDataMetaDataValueMetaDataValue 模型与 MetaData 模型有外键关系。

在一个简单的世界中,会有一个从MetaDataDocument的外键关系。然后 DocumentAdmin 会将 MetaData 字段显示为下拉列表,其中填充了适当的 MetaDataValues。下拉列表旁边还会有 edit/add (pencil/plus) 链接,以允许用户 edit/add 那个 MetaData 对象的 MetaDataValue

但是,该项目的要求之一是在 运行 之前不定义哪些或多少 MetaData 对象将与特定的 Document 类型相关联。这些关联是通过另一组模型实现的。因此,Document 管理更改页面在 运行 时将不同的 MetaData 字段添加到更改管理页面,方法是查看与此关联的 MetaData 字段的数据库Document 的类型,然后通过 get_fieldsets 方法将它们添加到字段集中。

这种设计的含义是不能 edit/add 值到 Document 管理更改页面上的特定 MetaData 字段,因为,我假设,Django 丢失了底层的外国DocumentMetaData 模型之间的关键关系,因为字段集是在管理员的 get_fieldsets 方法中生成的。

我可以添加几页代码来显示 MetaData 字段是如何在 运行 时间生成的,但我认为这不会使描述更清楚。我已经查看了为上述“简单世界”示例生成的页面源和字段集,但看不到 Django 在哪里确定何时将 edit/change 链接添加到特定外键字段的下拉列表中。

我的问题是,如何将铅笔和加号添加到 Document 管理更改页面中显示的这些 MetaData 字段,并让用户选择 add/edit该特定 MetaData 对象的 MetaDataValue?我可以只创建一些 Ajax 调用并自己完成所有繁重的工作,但我更愿意尽可能多地利用 Django 基础架构,而不是重新发明不必要的东西。

谢谢!

马克

我找到了使用 RelatedFieldWidgetWrapper 的解决方案。我将这个小部件包装器添加到我所有需要绿色加号(添加)和黄色铅笔(编辑)的选择中。

这里 post 的代码太多了,但这是它的核心。我的数据库 table MetaData 有所有字段的名称,MetaDataValue 有每个元数据字段的值。元数据用于描述文档(文本、视频、图像),但MetaData模型和Document模型之间没有FK关系。元数据和文档之间的关系包含在另一个 table 的 JSON 字段中。 MetaData table 还具有用于该元数据字段的 Django 字段类型。还有另一个 table 确定哪些元数据字段适用于哪种文档类型(图像、文本、视频)。对于所有 ModelChoiceFields,我添加了字段和小部件(Select 包装在 RelatedFieldWidgetWrapper 中)。

elif (metadata_names[i].field_type == MetaData.MODELCHOICEFIELD):
            fields[metadata_names[i].name] = forms.ModelChoiceField(queryset=MetaDataValue.objects.filter(metadata_id=metadata_names[i].metadata_id).order_by('value'), required=False, label=metadata_names[i].label, help_text=metadata_names[i].help_text)
            # add the green plus (add) and pencil (edit) links to each select field
            fields[metadata_names[i].name].widget = RelatedFieldWidgetWrapper(fields[metadata_names[i].name].widget, MetaDataValue._meta.get_field('tag_value'), admin_site, can_add_related=True, can_change_related=True)  
            if 'documentType_id' in fields:
                # Editing/add a new document type seems like a bad idea, as document type is used a lot in the processing logic
                fields['documentType_id'].widget.can_add_related = False
                fields['documentType_id'].widget.can_change_related = False

困难的部分是为 RelatedFieldWidgetWrapper 获取正确的参数。该小部件的文档有点稀疏,因为我认为它是管理的早期部分,但自首次实施以来,大部分 Django 代码都发生了变化,因此过去使用该包装器的方式有很多(即堆栈溢出 posts)已被弃用。我只是查看了源代码,并尝试了一些似乎合适的方法,直到它起作用为止。最后,这些文章帮了大忙: