1

I have a CarType that has a ForeignKey BodyMaterial in my models.py:

class BodyMaterial(models.Model):
    location                = models.ForeignKey('CarType')
    name                    = models.CharField(max_length=255)

class CarType(models.Model):
    name                    = models.CharField(max_length=255)
    default_body_material   = models.ForeignKey(BodyMaterial, null = True, blank = True, default = "", limit_choices_to={'location__exact': 1})

BodyMaterial is an Inline in CarType in my admin.py:

class BodyMaterial_Inline(admin.StackedInline):
    model = BodyMaterial
    extra = 1

class CarType_Admin(admin.ModelAdmin):
    inlines = [BodyMaterial_Inline]

admin.site.register(CarType, CarType_Admin)

I would like to filter the ForeignKey for default_body_material to show only the relevant BodyMaterials (the ones that appear/added on the same admin page). For example, I created a 2 seat CarType and in the same page added some BodyMaterials. Then I create an SVU CarType and some other BodyMaterials. When I go back to the 2 seat CarType, I would like to see only the relevant BodyMaterials in the drop-down for default_body_material.

I try to filter using limit_choices_to on the id. So I'm doing this using post_init because the id for the object in determined in runtime:

def setID(**kwargs):
    instance = kwargs.get('instance')
    default_body_material = instance._meta.get_field_by_name('default_body_material')[0]
    default_body_material.limit_choices_to = {'location__exact': instance.id}

post_init.connect(setID, CarType)

Unfortunately, that does nothing. What am I missing? Is there a beter why of filtering ForeignKey for my purposes (this is probably very basic)?

Note that this question is only for the admin interface.

2 Answers2

0

Just use a custom ModelForm:

class CarTypeAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(CarTypeAdminForm, self).__init__(*args, **kwargs)
        # Can't limit it until instance has been saved at least once
        if self.instance.pk:
            self.fields['default_body_material'].queryset = \
                self.fields['default_body_material'].queryset \
                    .filter(location=self.instance)

class CarTypeAdmin(admin.ModelAdmin):
    form = CarTypeAdminForm
    ...
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • I do have one question. When I run instance.bodymaterial_set.all(), even without the code you suggested, it prints out the correct foregnkeys. I'm not too familiar with Django to understand how Django knows without me explicitly declaring a queryset, but it does. I wonder if there's an easier way for doing the above without queryset in forms. – user1549769 Jul 25 '12 at 15:02
  • The key is that you have to actual store a new filtered queryset in place of the fields current queryset. You could just as easily do `self.fields['default_body_material'].queryset = self.instance.bodymaterial_set.all()`. However, filtering the queryset that's already assigned, as I've done, is more robust, as it carries over changes from any subclasses of the form, etc., whereas using this code would just flat out replace it. – Chris Pratt Jul 25 '12 at 15:07
0

You want to look at overriding the queryset function for your inline.

Erik
  • 7,479
  • 8
  • 62
  • 99