Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 416
renno Avatar asked Sep 18 '18 12:09

renno


1 Answers

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.

like image 79
DM Graves Avatar answered Sep 30 '22 18:09

DM Graves