Creating a DetailView of a profile with a queryset of posts created by that user
Question:
I’m creating a twitter-like app and I’m stuck on creating a UserProfileView which is supposed to display a certain User’s profile, along with a list of posts made by that user below.Though I can’t really figure out a way to create a proper view for that.
I’m trying to use class based views for that, the one I’ll be inheriting from is probably DetailView (for profile model) and something inside of that which retrieves a queryset of posts made by that user –
My profile model looks like this:
class Profile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
display_name = models.CharField(max_length=32)
profile_picture = models.ImageField(
default='assets/default.jpg', upload_to='profile_pictures')
slug = models.SlugField(max_length=150, default=user)
def get_absolute_url(self):
return reverse("profile", kwargs={"pk": self.pk})
Post model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateField(auto_now_add=True)
content = models.TextField(max_length=280)
image = models.FileField(upload_to='post_images/', blank=True, null=True)
def __str__(self) -> str:
return f'Post by {self.author} on {self.date_posted} - {self.content[0:21]}'
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.pk})
I’ve tried creating this method:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author=Profile.user)
return context
But this one sadly doesn’t work, raising an error of
"TypeError: Field 'id' expected a number but got <django.db.models.fields.related_descriptors.ForwardOneToOneDescriptor object at 0x000001A5ACE80250>."
‘ForwardOneToOneDescriptor’ object has no attribute ‘id’ is returned if I replace the filter argument with author=Profile.user.id
I’m not sure whether it’s a problem with the way I filtered Posts, or how I used get_context_data.
I’ve been stuck on this for a lot of time now and I feel very frustrated, please help me out.
Answers:
The object is stored as self.object
, so you can filter with:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author_id=self.object.user_id)
return context
An alternative might be to use a ListView
for the Post
s instead, to make use of Django’s pagination:
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
class UserProfileView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'users/profile.html'
paginate_by = 10
def get_queryset(self, *args, **kwargs):
return (
super()
.get_queryset(*args, **kwargs)
.filter(author__profile__slug=self.kwargs['slug'])
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['profile'] = get_object_or_404(Profile, slug=self.kwargs['slug'])
return context
Note: It is normally better to make use of the settings.AUTH_USER_MODEL
[Django-doc] to refer to the user model, than to use the User
model [Django-doc] directly. For more information you can see the referencing the User
model section of the documentation.
I’m creating a twitter-like app and I’m stuck on creating a UserProfileView which is supposed to display a certain User’s profile, along with a list of posts made by that user below.Though I can’t really figure out a way to create a proper view for that.
I’m trying to use class based views for that, the one I’ll be inheriting from is probably DetailView (for profile model) and something inside of that which retrieves a queryset of posts made by that user –
My profile model looks like this:
class Profile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
display_name = models.CharField(max_length=32)
profile_picture = models.ImageField(
default='assets/default.jpg', upload_to='profile_pictures')
slug = models.SlugField(max_length=150, default=user)
def get_absolute_url(self):
return reverse("profile", kwargs={"pk": self.pk})
Post model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateField(auto_now_add=True)
content = models.TextField(max_length=280)
image = models.FileField(upload_to='post_images/', blank=True, null=True)
def __str__(self) -> str:
return f'Post by {self.author} on {self.date_posted} - {self.content[0:21]}'
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.pk})
I’ve tried creating this method:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author=Profile.user)
return context
But this one sadly doesn’t work, raising an error of
"TypeError: Field 'id' expected a number but got <django.db.models.fields.related_descriptors.ForwardOneToOneDescriptor object at 0x000001A5ACE80250>."
‘ForwardOneToOneDescriptor’ object has no attribute ‘id’ is returned if I replace the filter argument with author=Profile.user.id
I’m not sure whether it’s a problem with the way I filtered Posts, or how I used get_context_data.
I’ve been stuck on this for a lot of time now and I feel very frustrated, please help me out.
The object is stored as self.object
, so you can filter with:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author_id=self.object.user_id)
return context
An alternative might be to use a ListView
for the Post
s instead, to make use of Django’s pagination:
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
class UserProfileView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'users/profile.html'
paginate_by = 10
def get_queryset(self, *args, **kwargs):
return (
super()
.get_queryset(*args, **kwargs)
.filter(author__profile__slug=self.kwargs['slug'])
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['profile'] = get_object_or_404(Profile, slug=self.kwargs['slug'])
return context
Note: It is normally better to make use of the
settings.AUTH_USER_MODEL
[Django-doc] to refer to the user model, than to use theUser
model [Django-doc] directly. For more information you can see the referencing theUser
model section of the documentation.