0👍
First, your model in models.py
file does not need a getter function for every attribute. You can just reference the individual attribute from an object using the .
notation like post.id
where post
is an object from the Post
model.
Your models.py
can just define the attributes like:
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.CharField(max_length=1000)
published_date = models.DateTimeField('date published')
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
I’ve changed the naming of attributes here based on the following best practices:
- The namespace for the model indicates
post
so, no need to name attributes prefixing the same name. - Using meaningful elaborated names like
published_date
andcreated_by
so that they are easier to read and gain context for anyone.
Your through-table or join-table relationship should have a Foreign-Key relationship to both tables like:
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='likes')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='likes')
For getting the count of likes, you don’t need the .all()
method on the manager. That returns all the likes of that post (if you have 10K likes on the post, it will bring all of those in the memory), but what you really want is to count, so you can do post.likes.count()
. You can also annotate the number of likes on your queryset.
The final view should look something like this:
from django.views import generic
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from django.db.models import Count
from .models import Post
def index(request):
posts = Post.objects.order_by("-published_date").annotate(num_likes=Count("likes"))
return render(request, 'home.html', {'posts': posts})
You can then use posts
directly in your template and use {{ post.num_likes }}
to display the no of likes on that post.
1👍
The modeling of the Like
is strange, a Like
should likely refer to a single item with a single Post
, so:
from django.conf import settings
class Like(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='likes')
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='likes'
)
So a Like
can refer to a combination of Post
and user, and thus each Post
can have multiple Like
s and a user can have multiple Like
s as well.
as for the the Post
model, in Python typically one does not write getters: the attributes can be retrieved just as attributes. So the post model can look like:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.CharField(max_length=1000)
pub_date = models.DateTimeField('date published')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
In the view, you also do not need to "serialize" the data, you can just pass the model objects to the template:
from django.db.models import Prefetch
def index(request):
posts = Post.objects.prefetch_related(
Prefetch('like', Like.objects.select_related('user'))
).order_by('-pub_date')
return render(request, 'home.html', {'posts': posts})
In the template you just render the data:
{% for post in posts %}
{{ post }}
likes:
<ul>
{% for like in post.likes.all %}
{{ like.user }}
{% endfor %}
</ul>
{% endfor %}