Django. Confusing query. Count items - django

I have table cold Mark.
class Mark(models.Model):
media = models.ForeignKey('media.Media')
mark = models.PositiveIntegerField()
user = models.ForeignKey(User)
class Meta:
unique_together = ('media_object','user')
How can i get query set of media instances (or just count the media) which has at least one vote?
Can i make it whith out using extra?
UPDATED:
I other words: I'm running through all table and counting all unique media. If i found it second time I'm not counting it.
Other words: I need count unique media fields.

I'm assuming the "Mark" model is how users vote. To get all media models with their mark counts, you need aggregation:
from django.db.models import Count
media_with_vote_count = Media.objects.annotate(vote_count=Count('mark_set'))
You can then use filter() that refers to that annotation:
voted_media = media_with_vote_count.filter(vote_count__gt=0)
There are also other useful aggregates. For example, you could calculate an average mark for every media:
from django.db.models import Avg
media_with_markavg = Media.objects.annotate(average_mark=Avg('mark_set__mark'))

mk = Mark.objects.all()
mk.media.count()
U can use the count function but not sure of ur question what u want to do from it or what is vote..
EDIT:
One row of media
if( mk.media.count() > 0):
......

Related

Django ORM: Get all records and the respective last log for each record

I have two models, the simple version would be this:
class Users:
name = models.CharField()
birthdate = models.CharField()
# other fields that play no role in calculations or filters, but I simply need to display
class UserLogs:
user_id = models.ForeignKey(to='Users', related_name='user_daily_logs', on_delete=models.CASCADE)
reference_date = models.DateField()
hours_spent_in_chats = models.DecimalField()
hours_spent_in_p_channels = models.DecimalField()
hours_spent_in_challenges = models.DecimalField()
# other fields that play no role in calculations or filters, but I simply need to display
What I need to write is a query that will return all the fields of all users, with the latest log (reference_date) for each user. So for n users and m logs, the query should return n records. It is guaranteed that each user has at least one log record.
Restrictions:
the query needs to be written in django orm
the query needs to start from the user model. So Anything that goes like Users.objects... is ok. Anything that goes like UserLogs.objects... is not. That's because of filters and logic in the viewset, which is beyond my control
It has to be a single query, and no iterations in python, pandas or itertools are allowed. The Queryset will be directly processed by a serializer.
I shouldn't have to specify the names of the columns that need to be returned, one by one. The query must return all the columns from both models
Attempt no. 1 returns only user id and the log date (for obvious reasons). However, it is the right date, but I just need to get the other columns:
test = User.objects.select_related("user_daily_logs").values("user_daily_logs__user_id").annotate(
max_date=Max("user_daily_logs__reference_date"))
Attempt no. 2 generates as error (Cannot resolve expression type, unknown output_field):
logs = UserLogs.objects.filter(user_id=OuterRef('pk')).order_by('-reference_date')[:1]
users = Users.objects.annotate(latest_log = Subquery(logs))
This seems impossible taking into account all the restrictions.
One approach would be to use prefetch_related
users = User.objects.all().prefetch_related(
models.Prefetch(
'user_daily_logs',
queryset=UserLogs.objects.filter().order_by('-reference_date'),
to_attr="latest_log"
)
)
This will do two db queries and return all logs for every user which may or not be a problem depending on the number of records. If you need only logs for the current day as the name suggest, you can add that to filter and reduce the number of UserLogs records. Of course you need to get the first element from the list.
users.daily_logs[0]
For that you can create a #property on the User model which could look roughly like this
#property
def latest_log(self):
if not hasattr('daily_logs'):
return None
return self.daily_logs[0]
user.latest_log
You can also go a step further and try the following SubQuery inside Prefetch to limit the queryset to one element but I am not sure on the performance with this one (credits Django prefetch_related with limit).
users = User.objects.all().prefetch_related(
models.Prefetch(
'user_daily_logs',
queryset=UserLogs.objects.filter(id__in=Subquery(UserLogs.objects.filter(user_id=OuterRef('user_id')).order_by('-reference_date').values_list('id', flat=True)[:1] ) ),
to_attr="latest_log"
)
)

How to sort by the sum of a related field in Django using class based views?

If I have a model of an Agent that looks like this:
class Agent(models.Model):
name = models.CharField(max_length=100)
and a related model that looks like this:
class Deal(models.Model):
agent = models.ForeignKey(Agent, on_delete=models.CASCADE)
price = models.IntegerField()
and a view that looked like this:
from django.views.generic import ListView
class AgentListView(ListView):
model = Agent
I know that I can adjust the sort order of the agents in the queryset and I even know how to sort the agents by the number of deals they have like so:
queryset = Agent.objects.all().annotate(uc_count=Count('deal')).order_by('-uc_count')
However, I cannot figure out how to sort the deals by the sum of the price of the deals for each agent.
Given you already know how to annotate and sort by those annotations, you're 90% of the way there. You just need to use the Sum aggregate and follow the relationship backwards.
The Django docs give this example:
Author.objects.annotate(total_pages=Sum('book__pages'))
You should be able to do something similar:
queryset = Agent.objects.all().annotate(deal_total=Sum('deal__price')).order_by('-deal_total')
My spidy sense is telling me you may need to add a distinct=True to the Sum aggregation, but I'm not sure without testing.
Building off of the answer that Greg Kaleka and the question you asked under his response, this is likely the solution you are looking for:
from django.db.models import Case, IntegerField, When
queryset = Agent.objects.all().annotate(
deal_total=Sum('deal__price'),
o=Case(
When(deal_total__isnull=True, then=0),
default=1,
output_field=IntegerField()
)
).order_by('-o', '-deal_total')
Explanation:
What's happening is that the deal_total field is adding up the price of the deals object but if the Agent has no deals to begin with, the sum of the prices is None. The When object is able to assign a value of 0 to the deal_totals that would have otherwise been given the value of None

Is there a way to query over a range based key cached object in django redis cache?

I'm working on a game server matchmaking and I have a room object which has a range that has two field and other extra fields :
min_score = IntegerField (help = 'minimum score that user should have to join this room.')
max_score = IntegerField (help = 'maximum score that a user can have to join this room.')
I'm going to cache this object and then if a user requests to join a room with a range that users can join.
Is there a way that I can do something like below query on redis-cache?
Room.objects.filter(min_score__lte=user.score, max_score__gte=user.score)
I already have some algorithms that should do .get('key') n times.
But I wonder if there's a better solution.
You can split this up in two filters, like:
Room.objects.filter(min_score__lte=user.score, max_score__gte=user.score)
We thus specify that the min_score is smaller than or equal to user.score, and the max_score is greater than or equal to user.score.
Here we use the __lte [Django-doc] and __gte lookups [Django-doc]. You can use __lt [Django-doc] and __gt [Django-doc] if you want the ranges to be exclusive.
By using a db_index, you probably can boost the lookup a bit more:
from django.db import models
class Room(models.Model):
min_score = models.IntegerField(db_index=True)
max_score = models.IntegerField(db_index=True)

Query for top x elements in Django

I have two models such that
class JobTitle(models.Model):
name = models.CharField(max_length=1000)
class Employer(models.Model):
jobtitle = models.ForeignKey(JobTitle,unique=False,null=True)
As you see, one employer may have many jobtitles. I try to make a query to get top 5 employers whose number of job titles is maximum
How can I achive this is Django ?
Thanks
Employer.objects.values('id').annotate(jobtitle_count=Count('jobtitle')).order_by('-jobtitle_count')[:5]
from django.db.models import Count
Employer.objects.annotate(Count('jobtitle')).order_by('-jobtitle__count')[:5]

Counting objects within a Foreign Key and ManyToMany in Django

I'd like to count the objects I have listed.
I would like to count the number of issues within each title.
This is how my models.py are setup:
# Edited out
Views.py:
def titles (request):
all_titles = Title.objects.all().order_by('title')
num_titles = Title.objects.all().count()
latest_titles = Title.objects.order_by('-date_added')
title_a = Title.objects.filter(title__startswith='A')
title_b = Title.objects.filter(title__startswith='B')
......etc......
I need to do this for creators and characters too, except they are in a Many to Many relationship with Issue and the views essentially look the same as the def titles
Thank you.
To get the related issues for each title you can use the backwards relationship lookup. Since you did not specify a related name in the Issue model where you created the relationship, the lookup is performed using _set appended to the lower cased name of the related model. In your case issue_set.
some_title = Title.object.get(pk=1)
some_title.issue_set.count()
However, this is going to perform a db hit for every title you want to count.
You probably want to annotate the titles qs with the count. docs
from django.db.models import Count
titles_with_counts = Title.objects.all().annotate(issue_count=Count('issue__id'))
now each title in that qs has count accessible by using .issue_count
for title in titles_with_counts:
print title.title, title.issue_count