I am new in Django. I am trying to make a clone of craiglist. Currently I have to models: Posts and Media (images). One post can have many Media. I want to show first image that was posted by user when they create a post as well as info about post on the index page. Unfortunately I can't properly join Media when I refer to Posts and vice versa. How to properly show post and media instances in index template?
Models:
class Post(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=1500)
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
subcategory = models.ForeignKey(Subcategory, on_delete=models.SET_NULL, null=True)
price = models.DecimalField(max_digits=12, decimal_places=2)
city = models.CharField(max_length=200)
class Media(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
name = models.CharField(max_length=500, null=True, blank=True)
photo = models.ImageField(upload_to='photo/%Y/%m/%d/', null=True, blank=True)
Views:
def index(request):
posts = models.Media.objects.all()
paginator = Paginator(posts, 2)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'aggregator/index.html', {'page': page_obj})
index.html template:
{% for media in page %}
<div >
<div>
<p>Picture</p>
</div>
<div>
<h4><a href="{% url 'show_post' post_id=media.post.id %}">{{ media.post.title|upper }}</a></h4>
</div>
<div>
<h5>{{media.post.price}}$</h5>
</div>
<div>
<h6>{{media.post.city}}</h6>
</div>
</div>
{% endfor %}
So again. How to normally join first media related to the posts? Like in the attached screenshot.
I found a solution to get posts from Media:
posts = models.Media.objects.all()
instead of getting posts themselves (i know this is horribly wrong) because I do not understand how to join Media: select_related and prefetch_related don't work - there is an error like this:
posts = models.Post.objects.select_related('Media').all()
Invalid field name(s) given in select_related: 'Media'. Choices are: user, category, subcategory
CodePudding user response:
You can prefetch with:
def index(request):
posts = models.Post.objects.prefetch_related('media_set')
paginator = Paginator(posts, 2)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'aggregator/index.html', {'page': page_obj})
Then in the template render with:
{% for post in page %} <div > <div> {% if post.media_set.all.0.photo %} <img src="{{ post.media_set.all.0.photo.url }}"> {% endif %} </div> <div> <h4><a href="{% url 'show_post' post_id=post.pk %}">{{ post.title|upper }}</a></h4> </div> <div> <h5>{{ post.price }}$</h5> </div> <div> <h6>{{ post.city }}</h6> </div> </div> {% endfor %}
The .prefetch_related
is not even necessary, but it will slow the view down, because then it will make a query per Post
, not one extra query to fetch all Media
objects.