Reputation: 135
I have a blog page showing all the user's posts. Each post has a 'Category'.
(Ex: Post 1 --> category: general coding, Post 2 --> category: general coding, Post 3 --> category: web dev)
If I wanted to show all the categories that the user posted in
(Ex: User profile page listview--> Categories posted --> general coding, web dev )
, would I have to use a 'for loop' and put them in a list? Or is there a better way to do this.
posts = user.post_set.all()
category_list = []
for post in posts:
if post.category not in category_list:
category_list.append(post.category)
models.py
class Category(models.Model):
category_name = models.CharField(max_length=50, default='general coding', verbose_name="Categories")
class Post(models.Model):
title = models.CharField(max_length=100, help_text="100 characters or less")
content = models.TextField()
category = models.ForeignKey(Category, blank=True, null=True, on_delete=models.SET_NULL)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
liked = models.ManyToManyField(Profile, blank=True, related_name='likes')
views.py
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html'
context_object_name = 'posts'
paginate_by = 5
def get_queryset(self):
return (
super()
.get_queryset()
# Filter by author/user
.filter(author__username=self.kwargs.get('username')).order_by('-date_posted')
# Prefetch comment using a Prefetch object gives you more control
.prefetch_related(
Prefetch(
"comment_set",
# Specify the queryset to annotate and order by Count("liked")
queryset=Comment.objects.annotate(
like_count=Count("liked")
).order_by("-like_count"),
# Prefetch into post.comment_list
to_attr="comment_list",
)
)
)
def get_context_data(self, *args, **kwargs):
# context = super(UserPostListView, self).get_context_data(*args, **kwargs)
context = super().get_context_data(*args, **kwargs)
c_form = CommentModelForm(self.request.POST or None)
user = get_object_or_404(User, username=self.kwargs.get('username'))
category_set = set(user.post_set.all().values_list("category", flat=True).exclude(category=None))
users_categories = []
for each_category_id in category_set:
each_category = Category.objects.filter(id=each_category_id).first()
users_categories.append(each_category)
context['users_categories'] = users_categories
return context
thanks to tushortz for helping with this question I've achieved what I was going for with below:
def get_context_data(self, *args, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
category_set = set(user.post_set.all().values_list("category", flat=True).exclude(category=None))
users_categories = []
for each_category_id in category_set:
each_category = Category.objects.filter(id=each_category_id).first()
users_categories.append(each_category)
context['users_categories'] = users_categories
return context
Now just wondering if there's a way to optimize this.
user.post_set.all().values_list("category", flat=True).exclude(category=None)
returns a queryset. Ex: <QuerySet [17, 12, 8, 1, 7, 1, 1, 1, 1, 1]>.
In order to remove the duplicates I had to put them as a 'set' per below:
category_set = set(user.post_set.all().values_list("category", flat=True).exclude(category=None))
this returns the 'id' of each category in a set. Ex: {1,2,5,7}. The for loop makes them a 'Category' object again. This seems inefficient as well..
Upvotes: 0
Views: 72
Reputation: 5015
you can use .exclude()
. I would recommend avoid looping directly
Even better filter directly with something like.
(
Category.objects.filter(author_id=user.id) # filter by user
.exclude(category_name__iexact"category i want to exclude") # exclude other category from the list
.values_list("category_name", flat=True) # only return a list of category_names
)
yu can have a look at django queryset api for more information.
Upvotes: 1