How do I filter a field inside of an Inline using a form with a queryset that depends on the instance?

Hello!

I’m on django 1.11. I’m having a small issue here when trying to edit the init of a ModelForm, for using in the django admin. If I do something like this:

class SubscriptionAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SubscriptionAdminForm, self).__init__(*args, **kwargs)
        print self.fields
        self.fields['billing_address'].queryset = Address.objects.filter(
            contact__pk=self.instance.contact.id)

I have no issues at all, the idea is to filter the Address objects on my Subscription model, so only the Addresses for that subscription appear on a select box. The problem starts when I want to add this form to an Inline!. I have a model called SubscriptionProduct that will contain a Subscritpion and an Address object. Whenever I’ll try something like this:

class SubscriptionProductForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(SubscriptionProductForm, self).__init__(*args, **kwargs)
        self.fields['address'].queryset = Address.objects.filter(
            contact__pk=self.instance.subscription.contact.id)

    class Meta:
        model = SubscriptionProduct
        fields = '__all__'

It will tell me that:

RelatedObjectDoesNotExist at /blahblah/core/subscription/***/change/
SubscriptionProduct has no subscription.

Which of course it’s not true. SubscriptionProduct does have a subscription. If I use the same form on the ModelAdmin outside of the inline I won’t have this issue.

I’ve also noticed that this doesn’t happen when I try to use some hardcoded values to build my queryset, for instance if I did something like:

self.fields['address'].queryset = Address.objects.filter(
            contact__pk=self.instance.subscription.contact.id)

It would work like a charm.

Can anyone help me trying to make this work? I’ve tried thousand of things and I’m running out of ideas! Maybe someone recognizes this as a known programming mistake.

Thank you!

Update: My problem seems to be because the “extra” inlines have no instance, and therefore I have no idea how to get the right “subscription” object for them to be filtered.

Could solve it using various stackoverflow posts, will post the code here if anyone needs it in the future. In my TabularInline (admin.py) I added this code. The first function gets the parent object, the second function changes the field:

def get_parent_object_from_request(self, request):
    """
    Returns the parent object from the request or None.
    Note that this only works for Inlines, because the `parent_model`
    is not available in the regular admin.ModelAdmin as an attribute.
    """
    resolved = resolve(request.path_info)
    if resolved.args:
        return self.parent_model.objects.get(pk=resolved.args[0])
    return None

def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
    field = super(SubscriptionProductInline, self).formfield_for_foreignkey(
        db_field, request, **kwargs)
    if db_field.name == 'address':
        if request:
            contact = self.get_parent_object_from_request(request).contact
            field.queryset = field.queryset.filter(contact=contact)
    return field
1 Like

Hi @virusereturns thanks for the code !

Do you have the full code ? I don’t understand how the methods are called in the Inline.

(Trying to do something similar)

Thanks !