Home > OS >  Django: How to Join One-to-many relationship? / Show joined query in template
Django: How to Join One-to-many relationship? / Show joined query in template

Time:12-28

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.enter image description here

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.

  • Related