Как я могу оптимизировать код Django только под один запрос

У меня есть простой django проект, который отображает даты предыдущих бронирований на основе id. Однако в настоящее время выполняется 2 запроса. (N+1 sql запрос, где N - количество бронирований) Есть ли у вас идеи, как я могу оптимизировать этот код только до 1 запроса?

Это файл model.py

from django.db import models


class Rental(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class Reservation(models.Model):
    rental = models.ForeignKey(
        Rental, on_delete=models.CASCADE, related_name="reservations")
    checkin = models.DateField()
    checkout = models.DateField()

    def get_previous_reservation(self):
        latest_reservation = Reservation.objects.order_by('-checkout').filter(
            rental=self.rental, checkout__lt=self.checkout).first()
        if latest_reservation is not None:
            return latest_reservation.id
        return '-'

    def __str__(self):
        return f"({self.id}, {self.checkin}, {self.checkout})"

Это файл view.py -> В нем выполняются запросы

from django.views.generic import CreateView
from .models import Reservation
from .forms import ReservationForm


class HomeView(CreateView):
    template_name = "index.html"
    form_class = ReservationForm
    success_url = '/'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['reservations'] = Reservation.objects.all(
        ).select_related('rental')
        return context

Функция get_reservation в модели вызывается в файле шаблона здесь:

{% extends 'base.html' %}

{% block content %}
<h2>Reservations</h2>
<table border="1" style="border-collapse: collapse; margin-top: 15px;">
  <thead>
    <tr>
      <th>Rental_name</th>
      <th>ID</th>
      <th>Checkin</th>
      <th>Checkout</th>
      <th>Previous Reservation ID</th>
    </tr>
  </thead>
  <tbody>
    {% for reservation in reservations %}
      <tr>
        <td>{{ reservation.rental.name }}</td>
        <td>{{ reservation.id }}</td>
        <td>{{ reservation.checkin|date:'Y-m-d' }}</td>
        <td>{{ reservation.checkout|date:'Y-m-d' }}</td>
        <td>{{ reservation.get_previous_reservation }}</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

<h2>Add new reservation</h2>

<form action="." method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Submit</button>
</form>
{% endblock content %}

Единственная мысль - добавить его как часть первого запроса и убрать get_previous_reservation из вызова. Псевдокод:

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        # You have all reservations here.
        reservations = Reservation.objects.all(
        ).select_related('rental')

        # Since you have all reservations, work out the previous reservation
        # for each reservation.
        reservations_response = {}
        for r in reservations:
              related = reservations.order_by('-checkout').filter(
            rental=self.rental, checkout__lt=self.checkout).first()
              related_reservations[r.id] = related.

        context['reservations'] = reservations_response
        return context

Моя точка зрения заключается в том, что вам нужно сделать это после того, как вы сделаете первый запрос (мой код выше является только приблизительным, вы можете захотеть использовать пользовательский класс вместо диктанта, как я сделал. Надеюсь, это поможет.

Вернуться на верх