如何防止Django Admin中FK/MTM字段的自我(递归)选择

10 django many-to-many foreign-keys django-models django-admin

给定具有ForeignKeyField(FKF)或ManyToManyField(MTMF)字段的模型以及"self"的外键,如何在Django Admin(admin)中阻止自我(递归)选择.

简而言之,应该可以防止在管理员中自我(递归)选择模型实例.这适用于编辑模型的现有实例,而不是创建新实例.

例如,对新闻应用中的文章采用以下模型;

class Article(models.Model):           
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    related_articles = models.ManyToManyField('self')
Run Code Online (Sandbox Code Playgroud)

如果有3个Article实例(标题:a1-3),当Article通过管理员编辑现有实例时related_articles,默认情况下由html(多个)选择框表示该字段,该框提供所有文章(Article.objects.all())的列表.用户应该只能看到并能够选择Article除自身以外的实例,例如,当编辑Articlea1时,related_articles可以选择= a2,a3.

我现在可以看到有三种方法可以做到这一点,按优先顺序递减;

  1. 提供一种方法来设置查询集,在管理表单字段中提供可用选项related_articles(通过排除查询过滤器,例如Article.objects.filter(~Q(id__iexact=self.id)),从用户可以查看和选择的related_article列表中排除正在编辑的当前实例.创建/设置要使用的queryset可能发生在__init__自定义的构造函数()中Article ModelForm,或者通过某种动态limit_choices_to Model选项.这需要一种方法来获取正在编辑的实例以用于过滤.
  2. 覆盖或类的save_model功能以在保存实例之前检查并从中删除自身.这仍然意味着管理员用户可以查看和选择所有文章,包括正在编辑的实例(对于现有文章).Article ModelModelAdminrelated_articles
  3. 当需要在管理员外部使用时,例如模板,过滤掉自我引用.

理想的解决方案(1)目前可以通过管理员之外的自定义模型表单来完成,因为可以将正在编辑的实例的过滤的查询集变量传递给模型表单构造函数.问题是,您是否可以获得Article实例,即在创建表单之前对管理员进行"自我"编辑以执行相同的操作.

我可能会以错误的方式解决这个问题,但是如果您允许将FKF/MTMF定义为同一模型,则应该有一种方法可以让管理员做正确的事情 - 并防止用户选择自己将其排除在可用选项列表中.

注意:解决方案2和3现在可以做,并提供尝试避免得到这些作为答案,理想情况下我想得到解决方案1的答案.

小智 9

卡尔是正确的,这是一个剪切和粘贴代码示例,将进入 admin.py

如果你没有扎实的把握,我发现导航Django关系可能会很棘手,而且一个活生生的例子比"去读这个"(不是你不需要了解正在发生的事情)价值1000倍. .

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
            id__exact=self.instance.id)
Run Code Online (Sandbox Code Playgroud)


Car*_*yer 2

您可以在管理中使用自定义 ModelForm(通过设置ModelAdmin 子类的“form”属性)。因此,您可以像在其他地方一样在管理员中执行此操作。

  • @kojiro - 简单的事情应该是简单的,高级的事情应该是可能的。并非所有可能的功能都必须压缩到 limit_choices_to 中,正是因为您始终可以编写自己的 ModelForm 代码来执行您想要的任何操作。如果您有一个直观的语法建议,可以将其添加到 limit_choices_to 而不使其变得更复杂,欢迎您打开带有补丁的票证并提出建议。 (2认同)