ckp7blessed
ckp7blessed

Reputation: 135

Django quick quest on making queries for blog posts and categories

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

Answers (1)

tushortz
tushortz

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

Related Questions