Why Can't I Call a Model Method in my View?

I have this model method:

@property
  def avg_rating(self):
    review_ratings = 0
    orig_rating = self.rating
    review_ratings_count = self.review_set.count()

    if review_ratings_count > 0:
      for review in self.review_set.all():
        review_ratings += review.rating
        ratings_sum = review_ratings + orig_rating

      ratings_count = review_ratings_count + 1
      ratings_avg = ratings_sum / ratings_count
      avg_return = round(ratings_avg, 1)

      if avg_return == 10.0:
        avg_return = 10
      else:
        avg_return = avg_return

      return avg_return

    else:
      return self.rating

It’s creating an average on ratings posted in an Album object (when an Album is created) and in the Albums related Review objects.
I can call avg_rating in a Python shell like so:

>>> from base.models import Album, Review
>>> albums = Album.objects.all()
>>> for album in albums:
...     print(album.avg_rating)
... 
10
8.5
9
8.3
9.7
7.7

But I am unable to call avg_rating in my views.py file (using the same dot notation configuration: album.avg_rating):

highest_rated_albums = Album.objects.annotate(
    album_ratings=F('album.avg_rating')
  ).order_by('-album_ratings')[0:5]

Because avg_rating is not data in the database - it’s not a field in the Model - it’s Python code that exists within your Django project.

The ORM works by constructing an SQL statement that is sent to the database engine for execution. That SQL statement can only work with the data that exists in the database, because it’s being executed by the database engine - not Python.

So there isn’t a way to bring avg_rating over to a view?

You can use avg_rating in the view.

What you can’t do is identify it as a field in an ORM query - which you can’t do anywhere. You can’t do that in the model, you can’t do that in a form, you can’t do that in a signal handler.

Can you direct me to documentation which shows how to use a model method in a view?

It’s an attribute - or in this case, a property of an instance of a model. You use it the same way you use any other field of a model in a view.

You’re using it in your example that you posted above. If you put that code in a view, it would work.

When I use avg_rating as an attribute in the view function above, I get this error:
" FieldError at /Cannot resolve keyword ‘album.avg_rating’ into field."

How are you trying to use it?

If you’re trying to use it in a query, it’s not going to work as explained above.

If you use it in something like the print function in your shell example, it would work.

It is not a field in the database, it makes no sense to try to reference it in a query.

I want to be able to use it to create a “Highest Rated” list of the albums based on the avg_rating attribute. Would I then have to delete the model method and recreate it as a view?

I guess that’s what I have to do?

No, that’s not going to matter.

If you have an instance of Album named album, then you can access album.avg_rating in your view. What you cannot do is pass that as a field to a query.

What and how you use that attribute in your view is up to you. But it needs to be used in the view, not in the database.

So I would have to put it in a variable, like album_avg_rating = album.avg_rating? Is it possible to iterate through each album to get each individual album’s avg_rating? Just like the for loop I have in my Python shell?

Yes, yes, and yes. That’s how you would use it in a view.