Django page reloading too long

Question:

I have some issues with the reloading time in some page in Django, it takes too long.
I use debug_toolbar and shows me Browser timing request: 4 (+113577)
enter image description here

models.py

class Subsidiary(models.Model):
    name = models.CharField(unique=True, max_length=100, blank=False)

    def __str__(self):
        return self.name

class Department(models.Model):
    name = models.CharField(unique=True, max_length=200, blank=False,)

    def __str__(self):
        return self.name

class SubBudget(models.Model):
    name = models.CharField(max_length=100, unique=True, blank=False)

    def __str__(self):
        return self.name

class SubBudgetOwner(models.Model):
    budget_owner = models.ForeignKey(Person, models.PROTECT, related_name="budgets_owned")
    sub_budget_owner = models.ForeignKey(Person, models.PROTECT, related_name="sub_budgets_owned")

    class Meta:
        ordering = ["id"]
        unique_together = (("budget_owner", "sub_budget_owner"),)
        verbose_name = "Budget Owner"  # Title on the maint page for this table
        verbose_name_plural = "Budget Owners"  # Title for the navbar navigation

    def __str__(self):
        return str(self.budget_owner) + " - " + str(self.sub_budget_owner)


class Combination(models.Model):
    id = models.AutoField(primary_key=True)
    subsidiary = models.ForeignKey(Subsidiary, verbose_name="Subsidiary", null=True, blank=True, on_delete=models.CASCADE,)
    department = models.ForeignKey(Department, verbose_name="Department", on_delete=models.CASCADE,)
    sub_budget = models.ForeignKey(SubBudget, verbose_name="Sub Budget", on_delete=models.CASCADE,)
    budget_owner = models.ForeignKey(SubBudgetOwner, verbose_name="Budget Owner & Sub Budget Owner", null=True, blank=True, on_delete=models.CASCADE,)

    def __str__(self):
        return (f"{self.subsidiary} {self.department} {self.sub_budget} {self.budget_owner}")

    class Meta:
        ordering = ["id"]

My views.py

class CombinationListView(LoginRequiredMixin, ListView):
    model = Combination
    template_name = "budget/combination_list.html"

combination_list.html

{% for combination in object_list %}
     <tr>
          <td>{{ combination.id }}</td>
          <td>{{ combination.subsidiary }}</td>
          <td>{{ combination.department }}</td>
          <td>{{ combination.sub_budget }}</td>
          <td>{{ combination.budget_owner }}</td>
     </tr>
{% endfor %}

I think maybe all the ForeignKeys do it so slow…

What can I do to improve it?

Asked By: user13719970

||

Answers:

You are making 11k queries in this page. This takes 46 seconds. You should start by lower the number of requests.

You should start by checking if you can use select_related or prefetch_related to improve the requests generated by your template.

Answered By: Nicolas Appriou

So I found a solution.
I added queryset function (CombinationQuerySet) and it makes a magic

In my model.py:

class CombinationQuerySet(models.QuerySet):
    def fast(self):
        return self.select_related(
            "subsidiary",
            "department",
            "sub_budget",
            "budget_owner__budget_owner",
            "budget_owner__sub_budget_owner",
        )

class Combination(models.Model):
    id = models.AutoField(primary_key=True)
    subsidiary = models.ForeignKey(Subsidiary, verbose_name="Subsidiary", null=True, blank=True, on_delete=models.CASCADE,)
    department = models.ForeignKey(Department, verbose_name="Department", on_delete=models.CASCADE,)
    sub_budget = models.ForeignKey(SubBudget, verbose_name="Sub Budget", on_delete=models.CASCADE,)
    budget_owner = models.ForeignKey(SubBudgetOwner, verbose_name="Budget Owner & Sub Budget Owner", null=True, blank=True, on_delete=models.CASCADE,)

    objects = CombinationQuerySet.as_manager()

    def __str__(self):
        return (f"{self.subsidiary} {self.department} {self.sub_budget} {self.budget_owner}")

    class Meta:
        ordering = ["id"]

my view.py (added the line: queryset = Combination.objects.fast()):

class CombinationListView(LoginRequiredMixin, ListView):
    model = Combination
    template_name = "budget/combination_list.html"
    queryset = Combination.objects.fast()

I saw it improved the page loading much faster (1 sec even less)!

Answered By: user13719970

def fast(self): is really helpful and used in my app.
return self.select_related was because a foreign keys and make it faster in the quary

Answered By: Itamar