how to filter by a specific user in list view

i am trying to write a logic that would display each user’s rating in list view not detail view. The Problem now is it displays the same rating for all the creator. When i try doing this in detail view it is quite easy becuase i have to firstly get the creator of the user like this

def CourseDetailView(request, course_slug):
    course = Course.objects.get(slug=course_slug)
    profile = Profile.objects.get(user=course.course_creator)
    creator_average = CreatorRating.objects.filter(profile=profile).aggregate(Avg('rating'))['rating__avg']

now when i pass in `creator_average` in the course detail page, it gets the exact rating for the user that created the course.

To achive the same result now in ListView seems difficult because instead of returning each user specific rating it just return the same rating for all the creators.

models.py

class CreatorRating(models.Model):
	user = models.ForeignKey(User, on_delete=models.CASCADE)
	profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, related_name="profile_rating")
	rating = models.DecimalField(max_digits=2, decimal_places=1, choices=CREATOR_RATING)
	review = models.TextField()
	date = models.DateTimeField(auto_now_add=True)
	active = models.BooleanField(default=False)
	
	def __str__(self):
		return f"{self.profile.full_name} - {self.rating}"
	
	class Meta:
		verbose_name = "Creator Rating"

views.py

creator_rating_average = CreatorRating.objects.filter(user=request.user).aggregate(Avg('rating'))['rating__avg']

What is your ListView creating a list of? (What are you listing?)

How is each element of that list related to the CreatorRating object?

1 Like
  1. I’m trying to list out all the creators from a Creator Model.

  2. I have a CreatorRating Model that is linked to Profile model through a foreignKey.

class Profile(m.M):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    full_name = models.TextField()
    

But how is the Creator model connected to any of these?

class Creator(models.Model):
	user = models.ForeignKey(User, on_delete=models.CASCADE)
	profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
	my_experience = models.TextField()
	date = models.DateTimeField(auto_now_add=True)
	active = models.BooleanField(default=False)

I’m listing out all the creators from the creator model, now I want to display the rating of a each creator under thier name.

It appears that you may have multiple redundant ForeignKey references.

Creator has FKs to both User and Profile.

CreatorRating has FKs to both User and Profile

Profile has an FK to User.

This structure means that Creator could have different CreatorRatings associated with it if there is an inconsistency between those reference sets.

Can a User actually be referenced by multiple Creator objects?
Does the Profile reference in CreatorRating have a different meaning than the reference to User?
Can a User have multiple Profile objects referring to it?

No, one user = one creator

Yes it does have different meaning, the User only stores the users account i.e username and password, while the Profile stores the images, full_name, bio and others.

No, a user is supposed to have only one profile

Ok, so the relationship between Creator and User should be a OneToOneField, not ForeignKey.

I understand the difference between the User and the Profile. What I am referring to is the relationship to each of them from the CreatorRating object.
The problem is you have two references in CreatorRating - one to a User and one to a Profile. Are they referring to two different people or the same person? (If they are to two different people, what’s the meaning of those two relationships. And if they’re supposed to refer to the same person, then you don’t want to have two fields, because they could end up referring to two different people, giving you different problems.)

Again, this should then be a OneToOne relationship, not a ForeignKey

They are referring to only one person not two

Okay I’m changing them now

So, does the ForeignKey affect the rating_average that I’m to trying to get one each users?

It does in that it removes the ambiguity that existed in the previous structure.

You have four different “paths” between tables (Creator → User → CreatorRating, Creator → Profile → CreatorRating, Creator → User → Profile → CreatorRating, and Creator → Profile → User → CreatorRating). Since those tables are related through ForeignKey relationships (meaning they can return multiple different elements), you could get different results based upon how the query is written.

There was actually no way to ensure you were going to get the “right” answer in your attempt to calculate those averages.

But in the detail view, I get the right answer which is the average_rating for each course creator, now in the list view: I don’t know how to still specifically get the average for each creators that I am listing out in the forloop. You know the details view have no forloop, so it made it a bit easier to get the averge_rating. Is There’s no way to query the filter to get the average_for each user in the list view I mean??

when I try quering like this -

creator = Creator.objects.filter(active=True)
creator_rating_average = CreatorRating.objects.filter(user=creator.user)

It shows that’s the query set should be limited to one slicing, I know that’s because I’m querying a filter by another filter and not a get. I don’t know how to achieve this??

Hopefully you’d understand what I’m trying to explain.

You are getting the right answer now because your database is still consistent. You probably don’t have any of the errors caused by inconsistent data possible with your previous data structures.

I do know what you’re trying to achieve here, but we needed to resolve those potential data problems first.

In the general sense, what you’re going to want to do is create your query and annotate (not aggregate) the queryset with the value you wish to obtain

In general terms, your query is going to look something like this:

creators = Creator.objects.filter(active=True).annotate(rating_average=Avg('user__creatorrating__rating'))

(This may not be 100% correct, but should give you a starting point.)

1 Like

Okay thanks, let me try this
But I would like to understand how it works, please what does annotate do there??

Review the docs at Aggregation | Django documentation | Django - especially the examples in the “Cheat Sheet” and this section: Generating aggregates for each item in a QuerySet

Okay sure, thanks. Let me review it right away