Choices on ForeignKey in Inlines not limited to the model constraints
|Reported by:||Stan||Owned by:||nobody|
|Severity:||Keywords:||inline foreignkey admin|
|Has patch:||no||Needs documentation:||no|
|Needs tests:||no||Patch needs improvement:||no|
I think the following behavior in the Admin is a bug. The following (simplified) model allow you to add Emails to an Advertiser for some of its Offices.
Everything take place in the Advertiser Change page :
- Add/delete offices via StackedInlines ;
- add/delete emails via TabularInlines.
# models.py class Address(models.Model): ... class Meta: abstract = True class Advertisor(Address): ... class AdvertisorOffice(Address): advertisor = models.ForeignKey(Advertisor) ... class EmailAdvertisor(models.Model): advertisor = models.ForeignKey(Advertisor) address = models.ForeignKey(Advertisor, blank=True, null=True) support = models.ForeignKey(Support, blank=True, null=True) ... # admin.py class AdvertisorAdmin(admin.ModelAdmin): ... inlines = [ AdvertisorOfficeInline, EmailAdvertisorInline, ... ] admin.site.register(models.Advertisor, AdvertisorAdmin) class AdvertisorOfficeInline(admin.StackedInline): model = models.AdvertisorOffice class EmailAdvertisorInline(admin.TabularInline): model = models.EmailAdvertisor extra = 1 ...
The thing is the select field to choose an Office in the Email inlines is not limited to the Offices of the current Advertisor. The full table is present in the choices and I have to make some Ajax to set the proper ones because overriding the queryset in the init() of a custom form is not possible, the advertisor_id not being present in the initial form attribute for some reasons.
Another side-effect is the impossibility to benefit of the overriding of the queryset method to make some select_related. My current workaround being a custom form and field.queryset redefined in the init :
class EmailAdvertisorForm(forms.ModelForm): class Meta: model = models.EmailAdvertisor def __init__(self, *args, **kwargs): super(EmailAdvertisorForm, self).__init__(*args, **kwargs) # We cannot use none() here despite the Ajax reset if we want a valid form. offices = AdvertisorOffice.objects.all().select_related('advertisor') if self.instance.pk and self.instance.advertisor: offices = self.instance.advertisor.officeadvertisor_set.all() self.fields["address"].queryset = offices