python - 如何在 Django 的管理员中设置关系字段的初始值?
问题描述
概括
我们想在 Django 管理员的基本“添加”或“更改”页面上设置代表不同类型模型关系的选择框的初始选择。如果用户单击“保存”按钮之一,则初始选择应保存到数据库中。
我们特别想通过initial
在表单字段(for ModelAdmin
or TabularInline
)上设置值来做到这一点。
问题是:
对于不同类型的关系表单字段Field.initial
,分配给的正确“值”是什么?
背景
假设我们有一个简单Model
的具有一个或多个 关系字段,即OneToOneField
,ForeignKey
或ManyToManyField
。默认情况下,后者有一个隐式 through
模型,但它也可以使用显式 through
模型,如docs中所述。显式through
模型是具有至少两个ForeignKey
字段的模型。
由选择框表示的关系字段自动出现在标准的“添加”或“更改”表单上,ModelAdmin
除了显式模型。那个需要内联,例如,如文档中所述。ManyToManyField
through
TabularInline
请注意,隐式 ManyToManyField
与ModelMultipleChoiceField
(admin) 表单上的 a 相关联,而其他关系字段与 a 相关联ModelChoiceField
。后者包括ManyToManyField
with显式 模型,through
因为它实际上由ForeignKey
.TabularInline
目标
现在考虑这个模型的基本 Django ModelAdmin
“添加”(或“更改”)页面,包括任何显式 through
模型的内联。下面描述了一个基于 Django 的 Pizza-Topping 的示例。
我们要实现两个目标:
- 为一个或多个关系字段设置初始选择
- 如果我们单击“保存”按钮之一,则必须将初始选择保存到数据库中(假设我们没有手动更改选择)
请注意,第二个似乎微不足道,但显然不是(见下文)。
方法
据我所知,有几种方法可以实现这一点:
- 设置模型字段
default
值,例如ForeignKey.default
,可选地传递一个可调用的(文档) - 设置
Form.initial
管理表单的值(文档) - 设置
Field.initial
管理表单域的值 ( docs )
无论哪种方法对于某个用例来说是“最好的”,这个问题都是关于最后一种方法:设置Field.initial
.
我们通过扩展ModelAdmin.formfield_for_dbfield
(或TabularInline.formfield_for_dbfield
)来做到这一点,如下所示:
def formfield_for_dbfield(self, db_field, request, **kwargs):
if db_field.name == 'some_relationship_field_name':
kwargs['initial'] = value
return super().formfield_for_dbfield(db_field, request, **kwargs)
本value
例中的 可以是obj.id
, obj
, [obj.id, ...]
, 或[obj, ...]
, 其中obj
是一个Model
实例。
问题
根据关系字段的类型,不同类型的value
可能会或可能不会起作用。
对于ManyToManyField
with 隐式through
模型,我们只能分配一个对象列表,这样很容易。
对于其他关系,在某些情况下,初始选择框选择与分配的值匹配,但不会保存(例如,因为Field.has_changed返回False
)。在其他情况下,初始选择框值被保存,但与分配的值不匹配。
问题
所以,显而易见的问题是:
对于管理表单上的不同类型的关系字段,value
分配给的正确方法是什么?Field.initial
有关的
尽管花了很多时间搜索,但我在文档、SO 和其他任何地方都找不到明确的答案。还试图通过源代码来解决这个问题,但事实证明这非常棘手。
一些类似的问题:
解决方案
下表显示了使用 Django 2.1 的专用最小示例的一些实验结果。根据表格,看来:
obj
或obj.id
只为OneToOneField
和工作ForeignKey
[obj.id]
适用于由 a 代表的所有关系ModelChoiceField
[obj]
仅适用于ManyToManyField
由 a 表示的隐式ModelMultipleChoiceField
但是,我不能说每种情况下的预期方法是什么。例如,为ForeignKey
字段分配列表没有多大意义。然而,有趣的是,即使ManyToManyField
with 显式through
模型具有与 a 的内联ModelChoiceField
a ForeignKey
,我们也不能只分配obj
or obj.id
:它必须在列表中。
任何评论都非常感谢这里。
表 1 的关键字:
- 1:选择框初始选择对应的值为
Field.initial
- 2:选择框初始选择被保存,点击“保存”后(注意:如果1失败,则选择框初始选择是第一个可用选项)
- T:(
TypeError
对象不可迭代) - A :
AttributeError
('int' 对象没有属性 'pk')
请注意,我们想要的行为是1 & 2。
Field.initial
表一:不同关系字段赋值结果
| | value assigned to Field.initial |
|-----------------|----------|--------------------------|-------|--------|-------|----------|
| model field | through | associated form field | obj | obj.id | [obj] | [obj.id] |
|-----------------|----------|--------------------------|-------|--------|-------|----------|
| OneToOneField | n/a | ModelChoiceField | 1 & 2 | 1 & 2 | 2 | 1 & 2 |
| ForeignKey | n/a | ModelChoiceField | 1 & 2 | 1 & 2 | 2 | 1 & 2 |
| ManyToManyField | explicit | ModelChoiceField | 1 | 1 | 2 | 1 & 2 |
| ManyToManyField | implicit | ModelMultipleChoiceField | T | T | 1 & 2 | A |
推荐阅读
- xamarin - 我的 Xamarin Forms Android 在高级选项中只有一个支持的 ABI。是否应该全部选中?
- performance - Tensorflow - 添加 Dropout 层显着增加推理时间
- http - If-None-Match 标头忽略 Content-Type 和 Vary
- scala - 无法识别的选项 -J-Xmx4G with sbt
- python - pandas 检查 DataFrame 中的单个值是否为数字
- vue.js - 如何使用 Vuejs 在另一个应用程序中导入应用程序
- javascript - 如何在数组中的两项后隐藏元素?
- shell - 在 POSIX Shell 中匹配扩展的正则表达式
- php - 隐藏 php 扩展名而不删除实际文件中的 .php
- python - 如何阻止向数据库python添加相同值的可能性?