这就是我的模型的样子:
class QuestionTagM2M(models.Model):
tag = models.ForeignKey('Tag')
question = models.ForeignKey('Question')
date_added = models.DateTimeField(auto_now_add=True)
class Tag(models.Model):
description = models.CharField(max_length=100, unique=True)
class Question(models.Model):
tags = models.ManyToManyField(Tag, through=QuestionTagM2M, related_name='questions')
我真正想做的就是在创建给定的manytomany关系时添加时间戳。这很有意义,但它也增加了一些复杂性。除了删除.add()功能之外[尽管事实上我真正添加的唯一字段是自动创建的,所以它在技术上不应该再干扰它了]。但我可以忍受这一点,因为我不介意做额外的QuestionTagM2M.objects.create(question=,tag=)
,如果这意味着获得额外的时间戳功能。我的问题是我真的很想能够在管理员中保留我的filter_horizontal
javascript小部件。我知道文档说我可以使用内联,但这实在太笨拙了,因为除了Tag
的外键之外,没有其他字段实际上在内联中。此外,在我的数据库模式的较大方案中,我的Question
对象已在我的管理页面上显示为内联,并且由于Django不支持admin [yet]中的嵌套内联,我无法使用为给定问题选择标签。有没有办法覆盖formfield_for_manytomany(self, db_field, request=None, **kwargs)
或类似的东西,以允许我使用漂亮的filter_horizontal
窗口小部件和自动创建date_added
列到数据库?这似乎是django应该能够本机化的东西,只要你指定中间的所有列都是自动创建的(除了外键)可能是auto_created=True
?或类似的东西
答案 0 :(得分:9)
方式可以执行此操作
QuestionTagM2M._meta.auto_created = True
并处理w / syncdb问题。将date_added
字段动态添加到models.py中的Question
模型的M2M模型中
class Question(models.Model):
# use auto-created M2M model
tags = models.ManyToMany(Tag, related_name='questions')
# add date_added field to the M2M model
models.DateTimeField(auto_now_add=True).contribute_to_class(
Question.tags.through, 'date_added')
然后您可以在管理员中将其用作正常ManyToManyField
在Python shell中,使用Question.tags.through
来引用M2M模型。
注意,如果您不使用South
,那么syncdb
就足够了;如果你这样做,South
不喜欢
这种方式并不会冻结date_added
字段,您需要手动编写迁移来添加/删除相应的列。
自定义ModelAdmin:
fields
,只定义filter_horizontal
。这将绕过Irfan回答中提到的现场验证。formfield_for_dbfield()
或formfield_for_manytomany()
以使Django管理员可以widgets.FilteredSelectMultiple
字段使用tags
。save_related()
方法,例如
def save_related(self, request, form, *args, **kwargs):
tags = form.cleaned_data.pop('tags', ())
question = form.instance
for tag in tags:
QuestionTagM2M.objects.create(tag=tag, question=question)
super(QuestionAdmin, self).save_related(request, form, *args, **kwargs)
__set__()
修补ManyToManyField的ReverseManyRelatedObjectsDescriptor
字段描述符date_added
以保存M2M实例,而不会引发异常。答案 1 :(得分:4)
自发布之前的答案以来,文档可能已发生变化。我看了一下@Irfan提到的django docs链接,它似乎比以前更直接。
向admin.py
添加内联类,并将模型设置为M2M模型
class QuestionTagM2MInline(admin.TabularInline):
model = QuestionTagM2M
extra = 1
在您的管理类中设置inlines
以包含您刚刚定义的内联
class QuestionAdmin(admin.ModelAdmin):
#...other stuff here
inlines = (QuestionTagM2MInline,)
不要忘记注册此管理类
admin.site.register(Question, QuestionAdmin)
完成上述操作后,当我点击一个问题时,我有表格对其进行所有正常编辑,下面是我的m2m关系中的元素列表,我可以在其中添加条目或编辑现有条目。
答案 2 :(得分:3)
使用ManyToManyField的through参数指定中间模型时,默认情况下管理员不会显示窗口小部件。这是因为该中间模型的每个实例都需要比单个窗口小部件中显示的信息更多的信息,并且多个窗口小部件所需的布局将根据中间模型而变化。
但是,您可以尝试在admin中使用fields = ('tags',)
明确地包含标记字段。这将导致此验证异常
'QuestionAdmin.fields'不能包含ManyToManyField字段'tags',因为'tags'会手动指定'through'模型。
此验证在https://github.com/django/django/blob/master/django/contrib/admin/validation.py#L256
中实施 if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
raise ImproperlyConfigured("'%s.%s' "
"can't include the ManyToManyField field '%s' because "
"'%s' manually specifies a 'through' model." % (
cls.__name__, label, field, field))
除非您实现自己的自定义字段以用作ManyToManyField,否则我认为您不能绕过此验证。