How do I display all the inline fields data associated with it's parent model field in template

Hey there! I’m new to pagination in django and I wanted to create a model which
a user can add multiple values in a field, so I searched the Internet and
stumbled upon using “admin.StackedInline” and “inlines” to a model. It worked
in the admin dashboard there are now options to add multiple data to inlines.

But the problem is, when I tried looping the querysets and paginating it I got the results (see images below) on the browser.

As you guys can see, even though the geohazard fields has multiple values, it only shows one inline data in the page (1st pic was barangay_img) & (2nd pic was geohazard_img). What I really want is to loop the entire inlines (geohazard_imgs) and display it in one page not separately with it’s associated parent field ‘history’. How am I going to achieve this in my templates and views?

Sorry for my confusing explanation I’m honestly a student and a complete django beginner.

models.py

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()

	def __str__(self):
		return f"{self.barangay_name} - GeoHazard History"

	class Meta:
		verbose_name = 'History'
		verbose_name_plural = 'Histories'
		ordering = ['barangay_name']
	
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()

	def __str__(self):
		return f"GeoHazard Details - {self.id}"

	class Meta:
		verbose_name = 'GeoHazard'
		verbose_name_plural = 'GeoHazards'


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')
	
	def __str__(self):
		return f"GeoHazard Assessment - {self.id}"

	class Meta:
		verbose_name = 'Assessment'
		verbose_name_plural = 'Assessments'
	

views.py

class history(ListView):
	model = GeoHazard
	template_name = 'auxiliary/history.html'
	context_object_name = 'geohazards'
	paginate_by = 1

template.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title></title>
</head>
<body>

	{% for hazard in geohazards %}
		<h1> {{ hazard.history.barangay_name }}</h1>
		<img src="{{hazard.history.barangay_img.url}}">
		<p>{{hazard.history.barangay_info}}</p>

		<img src="{{hazard.geohazard_img.url}}">
		<p>{{hazard.geohazard_info}}</p>

	{% endfor %}

	{% if is_paginated %}
		{% if page_obj.has_previous %}
			<button><a href="?page={{page_obj.previous_page_number}}">Previous</a></button>
		{% endif %}

		<button><a href="?page=1">1</a></button>
		<button><a href="?page=2">2</a></button>
		<button><a href="?page=3">3</a></button>
		<button><a href="?page=4">4</a></button>
		<button><a href="?page=5">5</a></button>
		<button><a href="?page=6">6</a></button>

		{% if page_obj.has_next %}
			<button><a href="?page={{page_obj.next_page_number}}">Next</a></button>
		{% endif %}

	{% endif %}
		
</body>
</html>

admin.py

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)

Been at it for ages and looking forward to anyone who can help. Really eager to find the solution to this for the Project’s deadline is right at our doorsteps. Thanks and advance!

First, what you create and build in your views has nothing to do with what you create and use in the Django Admin. The Django Admin classes are not used by your views. So trying to talk about “Inlines” from the Admin just confuses the issue when you’re really talking about views you’re using.

So let’s get back to fundamentals here.

My understanding is that on a page, you want:
One History object and all of the GeoHazard objects that relate to that History object. Is that correct?

If so, then your view should modify the queryset to use the prefetch_related method for the geoahazards related name, and then your template needs to iterate over the geohazards.all queryset on the History object being rendered to render all those instances of GeoHazard.

1 Like

Hey there! thanks for responding I really appreciate it. Yes I wanted to display objects in History and all of the objects in Geohazard model related to History model. I’m not familiar with prefetch_related though for I’m still starting to learn Django, but it would be of great help if you could direct me to some resources, forums, or anything that could help me understand prefetch_related. I know this is kinda abusive, but would you mind helping me out a little bit; by creating some sample code in my views that I could use as a starting point, I honestly don’t know where to start. Thank you so much for helping me out good Sir! Lookin forward to your response.

Actually, prefetch_related is (almost) never required. It is a performance-enhancement option to reduce the number of queries that Django will execute to retrieve your data if you are retrieving a set of the parent objects.

For example, if you’re querying for just one History object, prefetch_related isn’t going to improve anything. However, if you’re querying for multiple History objects, there would be a big difference.
Let’s say there are 100 History objects and you want all the Geohazard objects for each. In the default case, Django would execute 101 queries. One for the History object and one for each set of Geohazard objects related to that one History. However, if you use prefetch_related, Django will retrieve all of the Geohazard objects related to any of the retrieved History objects in one query - reducing the total to 2.

If you’re paginating by 1 for the History model, then you don’t need prefetch_related, since each page is only going to retrieve data for 1 History object.

1 Like

How bout inlineformset_factory or modelformset_factory? I’ve seen on YT you could display multiple fields together with its parent field in one page with it. Although I don’t think its completely gonna work in my case since it deals with fillable forms. And also I’m using inlines to make it easier for the user to navigate and add new data to fields in my admin.

Is there a way that I could keep the look of the admin and still be able to display want I want in my template? thanks!

Formsets (and InlineFormsets) are a way of presenting multiple instances of the same form on a page. They do not apply (are not needed) when just displaying information.

For the display, you can make it look however you want. It’s an issue of creating the HTML / CSS in your template.

Yes, you could use the same CSS that the Admin uses. Yes, you can create your HTML templates to resemble what the Admin generates.

You can read the Admin source code to see how it builds its pages and adopt or adapt whatever portions of it you care to use.

1 Like

Finally solved my problem by changing my views.py

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

and changing how I render it on the template.

{% 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 %}

Although I haven’t figured it out by myself, few folks from ‘Stack Oveflow’ actually helped me construct the code.

Thank you Sir! for pointing me to the right direction, especially on giving me the idea to use prefetch_related. Here’s the look of my template now:

I Also wanted to include your name in our documentations when we finished building the project if that’s ok to you Sir? Many Thanks!