Как правильно отобразить все инлайн-поля, связанные с родительскими полями модели, при пагинации в шаблоне Django?
Я студент и новичок в Django, и у нас есть проект, который мы пытаемся построить с помощью Django. На своем пути к созданию проекта я наткнулся на проблему и застрял на несколько недель.
Я хочу отображать все встроенные поля, связанные с родительским полем, на одной странице при постраничном просмотре. Когда я попытался постранично отобразить модель с 2 дополнительными моделями, имеющими внешние ключи к ней, я получил странный результат в моем шаблоне. Я не могу понять, как это исправить. Я попробовал несколько методов в Интернете и прочитал множество форумов и обсуждений, но безрезультатно, ни один из них до сих пор не сработал. Ниже приведены мои файлы и несколько изображений:
(models.py)
from django.db import models
class History(models.Model):
BARANGAY = (
('Alegria','Alegria'),
('Bagacay','Bagacay'),
('Baluntay','Baluntay'),
('Datal Anggas','Datal Anggas'),
('Domolok','Domolok'),
('Kawas','Kawas'),
('Ladol','Ladol'),
('Maribulan','Maribulan'),
('Pag-Asa','Pag-Asa'),
('Paraiso','Paraiso'),
('Poblacion','Poblacion'),
('Spring','Spring'),
('Tokawal','Tokawal')
)
barangay_name = models.CharField(max_length=100,choices=BARANGAY,default='Alegria')
barangay_img = models.ImageField(upload_to='history_imgs',blank=True)
barangay_info = models.TextField()
class GeoHazard(models.Model):
history = models.ForeignKey(History,related_name='geohazards',on_delete=models.CASCADE)
geohazard_img = models.ImageField(upload_to='history_imgs',blank=True)
date_published = models.CharField(max_length=100, null=True)
geohazard_info = models.TextField()
class Assessment(models.Model):
RATINGS = (
('HIGH','HIGH'),
('HIGH (Mitigated)','HIGH (Mitigated)'),
('MODERATE','MODERATE'),
('MODERATE (Mitigated)','MODERATE (Mitigated)'),
('LOW','LOW'),
('UNKNOWN','UNKNOWN'),
)
history = models.ForeignKey(History,related_name='assessment',on_delete=models.CASCADE)
purok_name = models.CharField(max_length=50)
purok_coordinates = models.CharField(max_length=100,default='unknown')
flood_rating = models.CharField(max_length=100,choices=RATINGS,default='UNKNOWN')
landslide_rating = models.CharField(max_length=100,choices=RATINGS,default='UNKNOWN')
В моем models.py у меня есть родительская модель 'History' и две дополнительные модели 'GeoHazard' и 'Assessment', обе с внешними ключами.
(admin.py)
from django.contrib import admin
from auxiliary.models import (
History,
GeoHazard,
Assessment
)
class GeoHazardInline(admin.StackedInline):
model = GeoHazard
extra = 0
class AssessmentInline(admin.StackedInline):
model = Assessment
extra = 0
class HistoryAdmin(admin.ModelAdmin):
inlines = [GeoHazardInline,AssessmentInline]
admin.site.register(History,HistoryAdmin)
В моем admin.py я использую 'StackedInline'. Я структурировал его таким образом, чтобы родительская модель 'History' могла иметь несколько встроенных полей, связанных с ней.
(views.py #1)
class history(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
Изначально я использовал 'ListView', чтобы воспользоваться его встроенным методом пагинации 'paginate_by', но в результате шаблон получился таким (см. изображение ниже). Как вы можете видеть, инлайн поля также имеют paginated_by '1', а другие инлайн поля были отделены от первой страницы.
(views.py #2)
class HistoryView(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
def get_context_data(self, **kwargs):
context = super(HistoryView, self).get_context_data(**kwargs)
context.update({
'geohazards': GeoHazard.objects.all(),
'assessments': Assessment.objects.all()
})
return context
def get_queryset(self):
histories = History.objects.all()
return histories
Попробовал другой подход; теперь, имея 3 модели, переданные в моем 'ListView' путем переопределения 'context' с помощью get_context_data. При таком подходе все встроенные поля отображаются в моем шаблоне template-views2-A.png, но на этот раз возникает новая проблема, хотя все встроенные поля отображаются в шаблоне. Теперь при выборе новой страницы в моих кнопках пагинации родительское поле меняется template-views2-B.png. но встроенные поля остаются прежними.
Кроме того, я также попробовал 'GeoHazard.objects.filter(history_id=1)' при обновлении словаря 'context', но это не решение, поскольку это только захватывает встроенные поля из родительских полей с определенным id. Затем я попытался использовать пользовательские теги шаблонов, django custom template-tags, но это не сработало.
(template.html) Вот мой шаблон:
{% for history in histories %}
<div class="row m-0">
<div class="col-md-3 col-sm-12 mt-4">
<div class="card bg-transparent border-0">
<div class="car-body text-center">
<h3><u>{{ history.barangay_name}}</u></h3>
<img src="{{ history.barangay_img.url }}" width="180" height="180" class="rounded-circle">
</div>
</div>
</div>
<div class="col-md-8 col-sm-12 mt-4" style="display: grid;place-items:center;">
<div class="card bg-transparent border-0">
<div class="car-body">
<p style="text-align:justify;">{{ history.barangay_info}}</p>
</div>
</div>
</div>
</div>
<hr>
{% endfor %}
{% if geohazards %}
{% for hazard in geohazards %}
<div class="row m-0">
<div class="col-md-3 col-sm-12 mt-4">
<div class="card bg-transparent border-0">
<div class="car-body text-center">
<img src="{{hazard.geohazard_img.url}}" height="200" width="300">
</div>
</div>
</div>
<div class="col-md-8 col-sm-12 mt-4" style="display: grid;place-items:center;">
<div class="card bg-transparent border-0">
<div class="car-body">
<h4>{{hazard.date_published}}</h4>
<p style="text-align:justify;">{{hazard.geohazard_info}}</p>
</div>
</div>
</div>
</div>
{% endfor %}
<hr>
template-with-labels.png. На этом рисунке я обозначил все поля, которые я пытаюсь отобразить в своем шаблоне.
Был этим давно и с нетерпением жду любого, кто может помочь. Очень хочется найти решение, так как срок сдачи проекта уже на пороге. Спасибо и заранее!
Вы должны иметь возможность доступа к связанным объектам GeoHazard
и Assessment
по их related_name
:
{% for history in histories %}
{{ history.barangay_name}
{# other history information #}
{% for hazard in history.geohazards.all %}
{{ hazard.geohazard_info }}
{# other hazard information #}
{% endfor %}
{% for assessment in history.assessment.all %}
{{ assessment.purok_name }}
{# other assessment information #}
{% endfor %}
{% endfor %}
Получение атрибута, который определен related_name
, вернет экземпляр RelatedManager
, который имеет методы типа all()
(такие же, как objects
на модели).
Обратите внимание, что для того, чтобы это работало, вам не нужно добавлять никаких дополнительных вещей в контекст, но с точки зрения производительности имеет смысл использовать prefetch_related()
, иначе для каждого экземпляра History
выполняются дополнительные запросы для получения связанных объектов.
class HistoryView(ListView):
model = History
template_name = 'auxiliary/history.html'
context_object_name = 'histories'
paginate_by = 1
def get_queryset(self):
histories = super().get_queryset()
histories = histories.prefetch_related("geohazards", "assessment")
return histories