Django ForeignKey limit_choices_to a different ForeignKey id

后端 未结 3 1688
囚心锁ツ
囚心锁ツ 2020-12-15 08:58

I\'m trying to limit Django Admin choices of a ForeignKey using limit_choices_to, but I can\'t figure out how to do it properly.

This code does what I want if the ca

相关标签:
3条回答
  • 2020-12-15 09:44

    After hours of reading semi related questions I finally figured this out.

    You can't self reference a Model the way I was trying to do so there is no way to make django act the way I wanted using limit_choices_to because it can't find the id of a different ForeignKey in the same model.

    This can apparently be done if you change the way django works, but a simpler way to solve this was to make changes to admin.py instead.

    Here is what this looks like in my models.py now:

    # 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)
    

    I simply removed limit_choices_to entirely. I found a similar problem here with the solution posted by Kyle Duncan. The difference though is that this uses ManyToMany and not ForeignKey. That means I had to remove filter_horizontal = ('prefix',) under my class MovieCategoryAdmin(admin.ModelAdmin): as that is only for ManyToMany fields.

    In admin.py I had to add from django import forms at the top to create a form. This is how the form looks:

    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)
    

    And my 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
    

    This is exactly how Kyle describes it in his answer, except I had to add fields = ['prefix'] to the Form or it wouldn't run. If you follow his steps and remember to remove filter_horizontal and add the fields you're using it should work.

    Edit: This solution works fine when editing, but not when creating a new entry because it can't search for the category id when one doesn't exits. I am trying to figure out how to solve this.

    0 讨论(0)
  • 2020-12-15 09:47

    Another approach, if you don't want to add a custom ModelForm, is to handle this in your ModelAdmin's get_form() method. This was preferable for me because I needed easy access to the request object for my queryset.

    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
    
    0 讨论(0)
  • 2020-12-15 09:54

    I had the same question and your self-answer helped me get started. But I also found another post (question-12399803) that completed the answer, that is, how to filter when creating a new entry.

    In views.py

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

    In 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)
    

    So, whether adding a new customer or updating an existing customer, I can click on a link to go add a new address that will be assigned to that customer.

    This is my first answer on StackOverflow. I hope it helps.

    0 讨论(0)
提交回复
热议问题