Django Prefetch Related Issue - поймите это правильно
У меня есть следующая проблема - я хочу отобразить все связки с их компонентными отношениями в шаблоне:
Вот моя ORM-модель:
class Component(models.Model):
plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
plenty_var_id = models.CharField(max_length=120, default=None, unique=True)
description = models.TextField(max_length=1000)
category = models.ForeignKey(Category, on_delete=models.DO_NOTHING, null=False)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
def __str__(self):
return f"{self.category.name} - {self.plenty_var_number}"
class Bundle(models.Model):
active = models.BooleanField(default=True)
plenty_var_number = models.CharField(max_length=120, default=None, unique=True, null=True)
plenty_var_id = models.CharField(max_length=120, null=True, default=None)
car = models.ForeignKey(Car, on_delete=models.DO_NOTHING)
# m2m defined by BundleComponentRelation
components = models.ManyToManyField(Component, through="BundleComponentRelation")
linked_to_plenty = models.BooleanField(default=False)
price = models.DecimalField(max_digits=10, decimal_places=2, default=-1.00)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
class BundleComponentRelation(models.Model):
component = models.ForeignKey(Component, on_delete=models.DO_NOTHING)
bundle = models.ForeignKey(Bundle, on_delete=models.DO_NOTHING)
qty = models.IntegerField(default=1)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
Я играл с select_related
и prefetch_related
в моем представлении, чтобы передать их через контекст в шаблон, чтобы отобразить его для моих пользователей:
html-template:
{% for bundle in bundles %}
<tr>
<td><p class="fw-bold">{{ bundle.plenty_var_number }}</p></td>
<td>{{ bundle.price }}</td>
<td><p class="fw-bolder mb-0">{{ bundle.car }}</p>
<p class="mb-0">{{ bundle.car.roof_type }}</p>
<p class="mb-0">BJ: {{ bundle.car.production_start }}
- {{ bundle.car.production_end }}</p>
</td>
{# Bundle Component Relations here #}
<td style="font-size: 1em;">
<a href={% url "edit_bundle" bundle.pk %}><i
class="fas fa-xl fa-edit "></i></a>
<a href={% url "sync_bundle" bundle.pk %}><i
class="fas fa-xl fa-sync "></i></a>
</td>
</tr>
{% endfor %}
views.py
def bundle_view(request):
bundles = Bundle.objects.prefetch_related('components').all()
print(bundles[0].components)
return render(request, "all_bundles.html", context={"bundles": bundles})
Выход print(bundles[0].components)
- bundle.Component.None
Я понял прямое использование select_related
, но мне трудно понять обратное мышление prefetch_related
в моей ситуации.
Я думаю, что моя проблема заключается в синтаксисе поиска prefetch_related
, но я могу ошибаться.
Что я здесь упускаю?
Заранее спасибо.
Нет никаких проблем с вашим prefetch_related, но то, как вы хотите получить доступ к этим объектам, вы должны сделать bundles[0].components.all()
, потому что к объектам, извлеченным с обратной связью, можно получить доступ так же, как к полям M2M
Вы уверены, что это должно быть prefetch_related
? Я думаю, что это должно быть select_related
.
Я думаю, что вы должны использовать Bundle.objects.select_related('components').all()
и Component.objects.prefetch_related('bundle_set').all()
с вашими моделями. Но я не уверен.
А что насчет ошибки шаблона - вы должны использовать {% for component in bundle.components.all %}
в шаблоне.
И там та же проблема, mb поможет.
Причина, по которой это происходит, заключается в том, что __str__
из Component
обращается к Category
, следовательно, для каждого {{ comp_rel }}
, который вы отображаете, он будет делать дополнительный запрос.
Вы должны использовать Prefetch
объект [Django-doc] для получения Category
s в том же запросе, в котором вы получаете Component
s:
from app_name.models import Bundle, Component
from django.db.models import Prefetch
def bundle_view(request):
bundles = Bundle.objects.prefetch_related(
Prefetch('components', Component.objects.select_related('category'))
)
return render(request, 'all_bundles.html', {'bundles': bundles})