I have a simple django project which displays previous reservations dates based on id’s. However currently there are 2 requests being made. (N+1 sql requests, where N is the reservation’s count) Do you have any idea how i would be able to optimize this code to only 1 query?
This is the model.py file
JavaScript
x
26
26
1
from django.db import models
2
3
4
class Rental(models.Model):
5
name = models.CharField(max_length=100)
6
7
def __str__(self):
8
return self.name
9
10
11
class Reservation(models.Model):
12
rental = models.ForeignKey(
13
Rental, on_delete=models.CASCADE, related_name="reservations")
14
checkin = models.DateField()
15
checkout = models.DateField()
16
17
def get_previous_reservation(self):
18
latest_reservation = Reservation.objects.order_by('-checkout').filter(
19
rental=self.rental, checkout__lt=self.checkout).first()
20
if latest_reservation is not None:
21
return latest_reservation.id
22
return '-'
23
24
def __str__(self):
25
return f"({self.id}, {self.checkin}, {self.checkout})"
26
This is the view.py file -> Where the queries are being made
JavaScript
1
16
16
1
from django.views.generic import CreateView
2
from .models import Reservation
3
from .forms import ReservationForm
4
5
6
class HomeView(CreateView):
7
template_name = "index.html"
8
form_class = ReservationForm
9
success_url = '/'
10
11
def get_context_data(self, **kwargs):
12
context = super().get_context_data(**kwargs)
13
context['reservations'] = Reservation.objects.all(
14
).select_related('rental')
15
return context
16
The get_reservation function in the model is being called in the template file here:
JavaScript
1
36
36
1
{% extends 'base.html' %}
2
3
{% block content %}
4
<h2>Reservations</h2>
5
<table border="1" style="border-collapse: collapse; margin-top: 15px;">
6
<thead>
7
<tr>
8
<th>Rental_name</th>
9
<th>ID</th>
10
<th>Checkin</th>
11
<th>Checkout</th>
12
<th>Previous Reservation ID</th>
13
</tr>
14
</thead>
15
<tbody>
16
{% for reservation in reservations %}
17
<tr>
18
<td>{{ reservation.rental.name }}</td>
19
<td>{{ reservation.id }}</td>
20
<td>{{ reservation.checkin|date:'Y-m-d' }}</td>
21
<td>{{ reservation.checkout|date:'Y-m-d' }}</td>
22
<td>{{ reservation.get_previous_reservation }}</td>
23
</tr>
24
{% endfor %}
25
</tbody>
26
</table>
27
28
<h2>Add new reservation</h2>
29
30
<form action="." method="post">
31
{% csrf_token %}
32
{{ form.as_p }}
33
<button type="submit">Submit</button>
34
</form>
35
{% endblock content %}
36
Advertisement
Answer
My only thought is to add it as part of the first request and remove get_previous_reservation
from being called. Pseudocode-ish:
JavaScript
1
18
18
1
def get_context_data(self, **kwargs):
2
context = super().get_context_data(**kwargs)
3
4
# You have all reservations here.
5
reservations = Reservation.objects.all(
6
).select_related('rental')
7
8
# Since you have all reservations, work out the previous reservation
9
# for each reservation.
10
reservations_response = {}
11
for r in reservations:
12
related = reservations.order_by('-checkout').filter(
13
rental=self.rental, checkout__lt=self.checkout).first()
14
related_reservations[r.id] = related.
15
16
context['reservations'] = reservations_response
17
return context
18
My point is you need to do it after you make the first query (my code above is only approximate, you may want to use a custom class instead of a dict like I did. Hope it helps.