Dan Schien
Dan Schien

Reputation: 1412

Django limit_choices_to on ManyToMany related with Inline

I would like to use limit_choices_to to reduce the set of choices for the Django admin of Model with a ManyToMany Field when using an Inline.

Interestingly, the reason why I want to limit choices is for performance as I want to use a property of a parent model in the __str__ method of my related model class, and not limiting the choices causes prohibitively many SQL queries.

The following works

class ParentOfA(models.Model):
    name = models.CharField(max_length=50, null=True)


class A(models.Model):
    parent = models.ForeignKey(ParentOfA)

    def __str__(self):
        return "%s" % self.parent


class B(models.Model):
    a = models.ManyToManyField(A, limit_choices_to={"a__name":'parent name'})

If I don't use an Inline in the admin form for B (following the example in the docs).

E.g.

@admin.register(B)
class BAdmin(admin.ModelAdmin):
    pass

However, with the inline the limit_choices_to has no effect:

class BInline(admin.TabularInline):
    model = B.A.through

@admin.register(B)
class BAdmin(admin.ModelAdmin):
    inline = (BInline,)

Any suggestions?

Upvotes: 0

Views: 2960

Answers (1)

jenniwren
jenniwren

Reputation: 351

I don't know why limit_choices_to doesn't work for the inline field, I am having the same problem. While this should not be necessary, you can limit the queryset of a field in an inline using this answer: https://stackoverflow.com/a/4236159/1302095

This is an old answer to an old question, so I'm not sure if there are better ways in newer versions of django. I am using 1.8.3 and it works, which is what matters to me!

Code pasted here for reference:

class RoomInline(admin.TabularInline):
    model = Room

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        field = super(RoomInline, self).formfield_for_foreignkey(db_field, request, **kwargs)

        if db_field.name == 'inside_room':
            if request._obj_ is not None:
                field.queryset = field.queryset.filter(building__exact = request._obj_)  
            else:
                field.queryset = field.queryset.none()

        return field

class BuildingAdmin(admin.ModelAdmin):
    inlines = (RoomInline,)

    def get_form(self, request, obj=None, **kwargs):
        # just save obj reference for future processing in Inline
        request._obj_ = obj
        return super(BuildingAdmin, self).get_form(request, obj, **kwargs)

Upvotes: 1

Related Questions