Felipe
Felipe

Reputation: 17

Field return count of another table

I need that read_booksfield (in Purchased), return the Rating objects count.

Should return the number of objects according to the filter of the same collections of each book in Rating and the same user.

I don't know how to do this because Rating and Purchased are not directly dependent.

rating/models.py

class Rating(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    book = models.ForeignKey(Book, on_delete=models.CASCADE, null=False)
    rating = models.IntergerField(default=0)

book/models.py

class Books(models.Model):      
    collection = models.ForeignKey(Collection, on_delete=models.DO_NOTHING, blank=True, null=True)
    book = models.CharField(max_length=250, blank=True, null=True)

collection/models.py

class Collection(models.Model):
    title = models.CharField(max_length=50)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)

purchased_collections/models.py

class Purchased(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    collection = models.ForeignKey(Collection, on_delete=models.DO_NOTHING)
    date = models.DateTimeField(default=datetime.now) 
    read_books = models.IntegerField(default=0)

AFTER APPLICATION OF @ruddra 'S RESPONSE

The first option:

    def read_books(self):
        return Rating.objects.filter(user=self.user, book__collection=self.collection).count()

worked in models.py. But I wanted to run this function only when the user logs in. So I tried to put it in def login (request): in views.py, but, it is not based on a class, I can't use the (self). Always returns the error name 'self' is not defined.

So, I tried the secont @ruddra 's suggestion:

purchases = Purchased.objects.annotate(read_books=Count('collection__books__rating', filter=Q(collection__books__rating__user=F('user'))))
for item in purchases:
    print(item.read_books)

views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .login import login_user

from purchased.models import Purchased
from rating.models import Rating
from collection.models import Collection
from book.models import Books

from django.db.models import F, Count, Q

def login(request):
    if not request.user.is_authenticated:
        if request.method == 'POST':
            login_status = login_user(request)
            if login_status != 1:
                messages.error(request, 'Invalid')
                return redirect('login')
            else:
                messages.success(request, 'You are now logged in')

                purchases = Purchased.objects.annotate(read_books=Count('collection__books__rating', filter=Q(collection__books__rating__user=F('user'))))
                for item in purchases:
                   print(item.read_books)


                return redirect('home')

        else:
            return render(request, 'users/login.html')
    else:
        messages.error(request, 'You are already logged in')
        return redirect('index')

but ever return error: NameError: name 'collection__books__rating__user' is not defined.

The problem is to be able to filter each ratings objects by their own collection. Using (self) in models.py it works perfectly, but I can't make it work without (self).

Any other suggestions to make this count run only when the user logs in?

Upvotes: 0

Views: 76

Answers (2)

ruddra
ruddra

Reputation: 51948

Assuming you meant book = models.ForeignKey(Book, on_delete=models.CASCADE, null=False) in Rating model:

You can use a property method to return the count:

class Purchased(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    collection = models.ForeignKey(Collection, on_delete=models.DO_NOTHING)
    date = models.DateTimeField(default=datetime.now) 

    @property
    def read_books(self):
        return Rating.objects.filter(user=self.user, book__collection=self.collection).count()

Or better, if you use annotation(which will reduce DB hits and you can use that for querying):

from django.db.models import Count, F, Q

purchases = Purchased.objects.annotate(read_books=Count('collection__books__rating', filter=Q(collection__books__rating__user=F('user'))))

for item in purchases:
    print(item.read_books)

Upvotes: 1

Pedram
Pedram

Reputation: 3920

As mentioned in the comments, if you are using Book as the foreign key related model, then you can find out the count by:

class Purchased(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    collection = models.ForeignKey(Collection, on_delete=models.DO_NOTHING)
    date = models.DateTimeField(default=datetime.now) 
    read_books = models.IntegerField(default=0)

    def save(self):
        self.read_books = Rating.objects.filter(user=self.user, book__in=self.collection.books_set).count()
        return super().save()

Upvotes: 1

Related Questions