如何防止Django Admin中FK/MTM字段的自(递归)选择
给定一个带有ForeignKeyField(FKF)或ManyToManyField(MTMF)字段且foreignkey为“self”的模型,如何在Django Admin(Admin)中防止自我(递归)选择 简言之,应该可以防止在管理中自我(递归)选择模型实例。这适用于编辑模型的现有实例,而不是创建新实例 例如,以新闻应用程序中的文章为例,采用以下模型:如何防止Django Admin中FK/MTM字段的自(递归)选择,django,django-models,django-admin,foreign-keys,many-to-many,Django,Django Models,Django Admin,Foreign Keys,Many To Many,给定一个带有ForeignKeyField(FKF)或ManyToManyField(MTMF)字段且foreignkey为“self”的模型,如何在Django Admin(Admin)中防止自我(递归)选择 简言之,应该可以防止在管理中自我(递归)选择模型实例。这适用于编辑模型的现有实例,而不是创建新实例 例如,以新闻应用程序中的文章为例,采用以下模型: class Article(models.Model): title = models.CharField(
class Article(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
related_articles = models.ManyToManyField('self')
如果有3个文章
实例(标题:a1-3),当通过管理员编辑现有的文章
实例时,相关文章
字段默认由一个html(多个)选择框表示,该框提供所有文章的列表(Article.objects.ALL()
)。用户应仅查看并能够选择自身以外的文章
实例,例如,在编辑文章
a1时,相关文章
可供选择=a2、a3
我目前可以看到3种可能的方法来做到这一点,顺序是减少偏好
相关文章提供可用选项(通过排除查询筛选器,例如Article.objects.filter(~Q(id\u iexact=self.id))
从用户可以查看和选择的相关文章列表中排除正在编辑的当前实例。要使用的查询集的创建/设置可以在构造函数中进行(\uuuuuu init\uu
)自定义的文章模型形式
,或者通过某种动态的限制选择_至模型
选项。这需要一种方法来获取正在编辑的实例以用于筛选
Article model
或ModelAdmin
类的save\u model
功能,以检查并从相关文章中删除自身。这仍然意味着管理员用户可以查看和选择所有文章,包括正在编辑的实例(对于现有文章)
文章
实例,即在创建表单以执行相同操作之前,管理员正在编辑“self”
这可能是我用了错误的方法,但是如果允许您为同一模型定义FKF/MTMF,那么应该有一种方法让管理员做正确的事情,并通过将其排除在可用选项列表中来防止用户选择自己
注意:现在可以使用解决方案2和3,提供这些解决方案是为了尽量避免将它们作为答案,理想情况下,我希望获得解决方案1的答案。您可以在管理中使用自定义模型表单(通过设置)。因此,您在管理员中执行此操作的方式与在其他任何位置执行此操作的方式相同。Carl是正确的,这是一个剪切粘贴代码示例,将放入
admin.py
我发现,如果你对Django的关系没有一个扎实的把握,那么在Django的关系中导航可能会很棘手,一个活生生的例子可能比一个“去读这个”更有价值1000倍(并不是说你不需要理解正在发生的事情)
您还可以覆盖ModelAdmin的
get\u form
方法,如下所示:
def get_form(self, request, obj=None, **kwargs):
"""
Modify the fields in the form that are self-referential by
removing self instance from queryset
"""
form = super().get_form(request, obj=None, **kwargs)
# obj won't exist yet for create page
if obj:
# Finds fieldnames of related fields whose model is self
rmself_fields = [f.name for f in self.model._meta.get_fields() if (
f.concrete and f.is_relation and f.related_model is self.model)]
for fieldname in rmself_fields:
form.base_fields[fieldname]._queryset =
form.base_fields[fieldname]._queryset.exclude(id=obj.id)
return form
请注意,这是一个通用的解决方案,可以自动查找自引用模型字段并从所有字段中删除自引用:-)我喜欢在
保存()时检查的解决方案。
时间:
def保存(self,*args,**kwargs):
#调用full_clean(),然后调用clean()
self.full_clean()
return super().save(*args,**kwargs)
def清洁(自清洁):
obj=自我
parents=set()
虽然obj不是无:
如果父母中有obj:
引发ValidationError('循环错误',代码='无限循环')
父母。添加(obj)
obj=obj.parent
从技术上讲,我知道为什么不能这样做。实际上,我不明白为什么它从来没有被实现过。@kojiro-简单的事情应该是简单的,高级的事情应该是可能的。并非所有可能的特性都必须被限制在选择范围之内,正是因为您可以随时编写自己的模型代码来执行任何您想要的操作。如果您有一个直观的语法建议,可以添加它来限制您的选择,而不会使它变得更复杂,那么欢迎您打开一个补丁并提出建议。
def get_form(self, request, obj=None, **kwargs):
"""
Modify the fields in the form that are self-referential by
removing self instance from queryset
"""
form = super().get_form(request, obj=None, **kwargs)
# obj won't exist yet for create page
if obj:
# Finds fieldnames of related fields whose model is self
rmself_fields = [f.name for f in self.model._meta.get_fields() if (
f.concrete and f.is_relation and f.related_model is self.model)]
for fieldname in rmself_fields:
form.base_fields[fieldname]._queryset =
form.base_fields[fieldname]._queryset.exclude(id=obj.id)
return form