fetch Django model in many to many relationship my other model
Question:
I am trying to figure out how many objects of a djanog model have a many to many relationship with a spicific objects of another model.
my models.py is
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
post_title = models.CharField(max_length=200)
post_body = models.CharField(max_length=1000)
pub_date = models.DateTimeField('date published')
by = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.post_title
def get_id(self):
return self.id
def get_body(self):
return self.post_body
def get_date(self):
return self.pub_date
def get_name(self):
return self.post_title
def get_author(self):
return self.by
def get_likes(self):
return type(self).likes.all()
class Like(models.Model):
associated_post = models.ManyToManyField(Post)
associated_user = models.ManyToManyField(User)
and my view is
from django.views import generic
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from .models import Post
def index(request):
posts = []
for post in Post.objects.order_by('-pub_date'):
posts.append({'title': post.get_name(), 'author_id': str(post.get_author()), 'created': post.get_date(), 'body': post.get_body(), 'id': post.get_id()})
print(post.get_likes())
return render(request, 'home.html', {'posts': posts})
Basically the function post.get_likes
needs to return the number of likes that have a realtionship with the post.
I v’e read the django documentation on this topic, but I just can’t quite figure out what is actually going on in the example code.
Answers:
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 %}
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
and created_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.
I am trying to figure out how many objects of a djanog model have a many to many relationship with a spicific objects of another model.
my models.py is
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
post_title = models.CharField(max_length=200)
post_body = models.CharField(max_length=1000)
pub_date = models.DateTimeField('date published')
by = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.post_title
def get_id(self):
return self.id
def get_body(self):
return self.post_body
def get_date(self):
return self.pub_date
def get_name(self):
return self.post_title
def get_author(self):
return self.by
def get_likes(self):
return type(self).likes.all()
class Like(models.Model):
associated_post = models.ManyToManyField(Post)
associated_user = models.ManyToManyField(User)
and my view is
from django.views import generic
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from .models import Post
def index(request):
posts = []
for post in Post.objects.order_by('-pub_date'):
posts.append({'title': post.get_name(), 'author_id': str(post.get_author()), 'created': post.get_date(), 'body': post.get_body(), 'id': post.get_id()})
print(post.get_likes())
return render(request, 'home.html', {'posts': posts})
Basically the function post.get_likes
needs to return the number of likes that have a realtionship with the post.
I v’e read the django documentation on this topic, but I just can’t quite figure out what is actually going on in the example code.
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 %}
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.