Search code examples
djangoforeign-key-relationshipinline-formset

How do I filter a model field on a form that is a foreignkey but not the main foreignkey


I have an inlineformset_factory that is a group of ShoppingListItemForm

View:

class ShoppingListItemForm(ModelForm):
    @property
    def __name__(self):
        return self.__class__.__name__

    def __init__(self, *args, **kwargs):

        if kwargs.get('instance'):

            theList = kwargs['instance']
            self.fields['category'] = forms.ChoiceField(choices=[(cat.id, cat.name) for cat in ListItemCategory.objects.filter(shoppinglist__id=theList.id)])

        return super(ShoppingListItemForm, self).__init__(self, *args, **kwargs)



    class Meta:
        model = ShoppingListItem
        fields = ('item', 'brand', 'quantity', 'current', 'category', 'size', )


@login_required
def shoppinglist(request, shoppinglist_id, pod_id):
    profile = request.user.get_profile()
    shoppinglist = get_object_or_404(ShoppingList, pk=shoppinglist_id)

    ListFormSet = inlineformset_factory(ShoppingList, ShoppingListItem, form=ShoppingListItemForm, extra=1, can_delete=True)

    myForms = ListFormSet(instance=shoppinglist)

...This works great, except 'category' is a foreign key of ShoppingListItem, and I need to filter the 'category' options offered on my form to only those which are related to ListItemCategory by 'shoppinglist'. 'shoppinglist' is a foreign key of both ListItemCategory and ShoppingListItem.

Models:

class ListItemCategory(models.Model):
    name = models.CharField(max_length=30, blank=True, null=True)
    shoppinglist = models.ForeignKey(ShoppingList)

class ShoppingListItem(models.Model):
    shoppinglist = models.ForeignKey(ShoppingList)
    category = models.ForeignKey(ListItemCategory, blank=True, null=True, default = None)
    item = models.CharField(max_length=200, blank=True, null=True)

class ShoppingList(models.Model):
    name = models.CharField(max_length=30, blank=True, null=True, default="Pod List")

...I think there is no need to pass in the 'shoppinglist' as an extra argument, as it is passed as the instance of the form, but with this initialization added, my rendered template has nothing at all in the formset.

Any further advice?


Solution

  • Your issue is that you're not "initializing" the variable shoppinglist_id in your form def init

    Try this:

    class ShoppingListItemForm(ModelForm):
        class Meta:
            model = ShoppingListItem
            fields = ('item', 'brand', 'quantity', 'current', 'category', 'size', )
    
        def __init__(self, *args, **kwargs):
            shoppinglist_id = kwargs.pop('shoppinglist_id', None)
            super(ShoppingListItemForm, self).__init__(*args, **kwargs)
            self.fields['category'].queryset = ListItemCategory.objects.filter(shoppinglist__id=shoppinglist_id)
    

    And you should edit also how to initialize the form:

    ListFormSet = inlineformset_factory(ShoppingList, ShoppingListItem, form=ShoppingListItemForm(shoppinglist_id=shoppinglist.id), extra=1, can_delete=True)
    

    The changes made are the next ones:

    1. Pass the shoppinglist_id as a kwarg argument

    ...ShoppingListItemForm(shoppinglist_id=shoppinglist.id)

    1. Get the kwarg argument in your form

      def __init__(self, *args, **kwargs): shoppinglist_id = kwargs.pop('shoppinglist_id', None)