【问题标题】:Django ForeignKey limit_choices_to a different ForeignKey idDjango ForeignKey limit_choices_to a different ForeignKey id
【发布时间】:2015-10-13 05:44:19
【问题描述】:

我正在尝试使用 limit_choices_to 限制 Django 管理员对 ForeignKey 的选择,但我不知道如何正确地做到这一点。

如果类别 ID 为 16,则此代码执行我想要的操作,但我无法弄清楚如何使用当前类别 ID,而不是对其进行硬编码。

class MovieCategory(models.Model):    
    category = models.ForeignKey(Category)
    movie = models.ForeignKey(Movie)
    prefix = models.ForeignKey('Prefix', limit_choices_to={'category_id': '16'},
                               blank=True, null=True)
    number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
                                 blank=True, null=True, decimal_places=0)

是否可以以某种方式引用类别 ForeignKey 的 id?

【问题讨论】:

标签: python django


【解决方案1】:

在阅读了几个小时的半相关问题后,我终于弄明白了。

您不能按照我尝试的方式自行引用模型,因此无法使用 limit_choices_to 使 django 以我想要的方式运行,因为它无法在同一模型中找到不同 ForeignKey 的 id。

如果您更改 django 的工作方式,显然可以做到这一点,但解决此问题的更简单方法是更改​​ admin.py。

这是我的 models.py 现在的样子:

# models.py
class MovieCategory(models.Model):    
    category = models.ForeignKey(Category)
    movie = models.ForeignKey(Movie)
    prefix = models.ForeignKey('Prefix', blank=True, null=True)
    number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
                                 blank=True, null=True, decimal_places=0)

我只是完全删除了limit_choices_to。 我在 Kyle Duncan 发布的解决方案中发现了类似的问题 here。 不同之处在于它使用了 ManyToMany 而不是 ForeignKey。这意味着我必须在我的班级 MovieCategoryAdmin(admin.ModelAdmin): 下删除 filter_horizontal = ('prefix',),因为这仅适用于 ManyToMany 字段。

在 admin.py 中,我必须在顶部添加 from django import forms 才能创建表单。 这是表单的外观:

class MovieCategoryForm(forms.ModelForm):

    class Meta:
        model = MovieCategory
        fields = ['prefix']

    def __init__(self, *args, **kwargs):
        super(MovieCategoryForm, self).__init__(*args, **kwargs)
        self.fields['prefix'].queryset = Prefix.objects.filter(
                                        category_id=self.instance.category.id)

还有我的 AdminModel:

class MovieCategoryAdmin(admin.ModelAdmin):
    """
    Admin Class for 'Movie Category'.
    """
    fieldsets = [
        ('Category',      {'fields': ['category']}),
        ('Movie',         {'fields': ['movie']}),
        ('Prefix',        {'fields': ['prefix']}),
        ('Number',        {'fields': ['number']}),
    ]
    list_display = ('category', 'movie', 'prefix', 'number')
    search_fields = ['category__category_name', 'movie__title', 'prefix__prefix']
    form = MovieCategoryForm

这正是 Kyle 在他的回答中描述的方式,除了我必须在表单中添加 fields = ['prefix'] 否则它不会运行。如果您按照他的步骤操作并记住删除 filter_horizo​​ntal 并添加您正在使用的字段,它应该可以工作。

编辑:此解决方案在编辑时工作正常,但在创建新条目时却不行,因为当一个不退出时它无法搜索类别 ID。我正在想办法解决这个问题。

【讨论】:

  • 因为我一直很忙,所以我围绕创建新项目的问题编写了代码。我目前在类别模型而不是这个模型中创建新项目,并且由于 Prefix 有一个外键链接到它,所以我消除了这个问题。如果我确实找到了解决此问题的方法,我将更新我的解决方案,但就目前而言,这可以满足我的应用程序所需。
  • 你在哪里定义了 Prefix 模型?或者这是什么? self.fields['prefix'].queryset = Prefix.objects.filter( category_id=self.instance.category.id)
【解决方案2】:

如果您不想添加自定义 ModelForm,另一种方法是在您的 ModelAdmin 的 get_form() 方法中处理此问题。这对我来说更可取,因为我需要轻松访问我的查询集的请求对象。

class StoryAdmin(admin.ModelAdmin):

    def get_form(self, request, obj=None, **kwargs):
        form = super(StoryAdmin, self).get_form(request, obj, **kwargs)

        form.base_fields['local_categories'].queryset = LocalStoryCategory.\
            objects.filter(office=request.user.profile.office)

        return form

【讨论】:

    【解决方案3】:

    我有同样的问题,你的自我回答帮助我开始了。但我也找到了另一篇帖子(question-12399803)完成了答案,即新建条目时如何过滤。

    在views.py中

    form = CustomerForm(groupid=request.user.groups.first().id)
    

    在forms.py中

    def __init__(self, *args, **kwargs):
        if 'groupid' in kwargs:
            groupid = kwargs.pop('groupid')
        else:
            groupid = None
        super(CustomerForm, self).__init__(*args, **kwargs)
        if not groupid:
            groupid = self.instance.group.id
        self.fields['address'].queryset = Address.objects.filter(group_id=groupid)
    

    因此,无论是添加新客户还是更新现有客户,我都可以单击链接添加将分配给该客户的新地址。

    这是我在 StackOverflow 上的第一个答案。希望对你有帮助。

    【讨论】:

    • 否决我的人会给我留言告诉我原因吗?
    • 您可以将if/else 块替换为groupid = kwargs.pop('groupid', None),顺便说一句。
    【解决方案4】:

    请记住,limit_choices_to 支持“Either a dictionary, a Q object, or a callable returning a dictionary or Q object”并且理论上应该支持可以使用 django 的查询集过滤完成的任何查找。然后,一个潜在的解决方案是根据您控制的类别的某些属性(例如 slug 字段)进行过滤。

    class MovieCategory(models.Model):    
        category = models.ForeignKey(Category)
        movie = models.ForeignKey(Movie)
        prefix = models.ForeignKey('Prefix', blank=True, null=True,
                                   limit_choices_to=Q(category__slug__startswith='movie'))
        number = models.DecimalField(verbose_name='Movie Number', max_digits=2,
                                     blank=True, null=True, decimal_places=0)
    

    【讨论】:

    • 我如何使用limit_choices_to来引用父ID?
    • @Jcc.Sanabria 如果你有一个Book 属于Author 关系,并且你想过滤你的书选择给那些属于一个特定的作者,你应该能够做到limit_choices_to=Q(author_id=1)。如果您在 Author 上有一个 ManyToMany('myapp.Book') 字段,则它将类似于 limit_choices_to=Q(author__id=1),因为您使用的是反向访问器。有关更多信息,请阅读 ManyToMany 字段。但我不推荐按 id 硬编码过滤器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多