Как правильно отобразить все инлайн-поля, связанные с родительскими полями модели, при пагинации в шаблоне 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-dashboard.png

(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', а другие инлайн поля были отделены от первой страницы.

template-views1.png)


(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
Вернуться на верх