renno
renno

Reputation: 2827

Django admin slow using TabularInline with many to many field

I'm trying to use Django admin to visualize all samples in one request. It works, but it is really slow. I tried to use prefetch, formset, raw_id_fields, and readonly_fields with no luck. It is still very slow when I load more than 10 samples. I'm having a N+1 problem here. I checked on Django debug toolbar and it makes a query for each sample in that request.

Here are my models:

# This is where the sample has all information
class Inventory(models.Model):
    sample_id = models.CharField(max_length=50, primary_key=True)

    def __str__(self):
        return '{0}'.format(self.sample_id)

# Intermediate model
class SampleRequestInventory(models.Model):
    sample = models.ForeignKey("Inventory", on_delete=models.CASCADE)
    request = models.ForeignKey("SampleRequest", on_delete=models.CASCADE)

# This is the request model that I'm looking  
class SampleRequest(models.Model):
    samples = models.ManyToManyField("Inventory", through="SampleRequestInventory")

Here are my django admin configuration:

class SamplesInline(admin.TabularInline):
    model = SampleRequestInventory
    # raw_id_fields = ('sample',)
    readonly_fields = ('sample',)
    extra = 0

# this formset did not work either
# class MyInlineFormset(BaseInlineFormSet):
#      def __init__(self, data=None, files=None, instance=None,
#              save_as_new=False, prefix=None, queryset=None, **kwargs):
#         super(MyInlineFormset, self).__init__(data, files, instance,
#                                           save_as_new, prefix, queryset, **kwargs)
#         self.queryset = SampleRequest.objects.all(). \
#         prefetch_related('samples')

class SampleRequestAdmin(admin.ModelAdmin):
    # This queryset for prefetching only makes an extra query...
    def get_queryset(self, request):
        qs = super(SampleRequestAdmin, self).get_queryset(request)
        return qs.prefetch_related('samples')
    # extra settings
    list_display = ('date', 'status',)
    ordering = ('date',)
    # inline that causes the slowness
    inlines = (SamplesInline, )
    # I tried using formset as well, but didn't work
    # formset = MyInlineFormset

Upvotes: 5

Views: 2821

Answers (1)

DM Graves
DM Graves

Reputation: 819

Try adding the following to your SamplesInline:

def get_queryset(self, request):
    qs = super(SamplesInline, self).get_queryset(request)
    return qs.select_related('sample')

The readonly_fields in your Inline is causing the extra lookup to satisfy the __str__ for your Inventory class.

Upvotes: 7

Related Questions