Count foreign objects for model - django

I have following models (simplified) in Django app:
class Company(BaseModel):
user = models.OneToOneField(User)
title = models.CharField(max_length=128)
class Event(BaseModel):
company= models.ForeignKey(Company)
description = models.TextField(verbose_name="Description", blank=True,null=True,max_length=200,default="")
in one of my views, I want to count Event objects for company "owned" by logged user.
to access company object I use something like request.user.company
How can I count all Event objects related to single company?
EDIT:
I think I asked wrong question. What I want to do:
I select single event object:
event = Event.objects.get(uuid=event_uuid)
and now I want to get number of Event but within single company, not global ID.

I found hard to understand your question, do you want to count Event associated to a given Company?
c = Company.objects.get(...)
event_n = Event.objects.filter(company=c).count()
or
n = 2
event_n = Event.objects.filter(company__pk=n).count()
or
u = User.objects.get(...)
event_n = Event.objects.filter(company__user=u).count()
Do you want to collect one (some) company (companies) with the number of Event associated?
from django.db.models import Count
company = Company.objects.filter(pk=n).annotate(event_n=Count('event')).get()
print company.event_n
or
companies = Company.objects.filter(...).annotate(event_n=Count('event'))
for c in companies:
print c.event_n
If you already have an event and you want the number of events associated to its company you can try
e = Event.objects.get(...)
event_n = Event.objects.filter(company=e.company).count()
or
n = 3
e = Event.objects.filter(pk=n).annotate(event_n=Count('company__event')).get()
print e.event_n

Related

Django: Check if record exists in two different states

I am using two models to build a chat system between users:
class Chat(models.Model):
created = models.DateTimeField(auto_now_add=True)
class Participant(models.Model):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='participants')
sender = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
receiver = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
One record in the participants model represents the ability to send messages from the user sender to the user receiver.
Thus, for a valid private chat between A and B, two records will exist, one with A as sender and B as receiver and vice versa.
Since one user will always be the one starting the chat but the first participant record could be with A as sender or B as sender, I need to know if there's a clean and cheap way to check if both records exist when a user tries to initiate a chat, and return the chat id if it exists.
How do I search for the existence of records (sender=A, receiver=B) and (sender=B, receiver=A) in the same query?
You can use Q objects to create complex queries including matching on one condition OR another
query = Participant.objects.filter(Q(sender=A, receiver=B) | Q(sender=B, receiver=A))
query.count() == 2 # If you want to check that 2 records exist
| in this case creates a filter with an "OR"
You can make use of two JOINs here, like:
Chat.objects.filter(
participants__sender=user_a,
participants__receiver=user_b
).filter(
participants__sender=user_b,
participants__receiver=user_a
)
This will result in a query like:
SELECT chat.id, chat.created
FROM chat
INNER JOIN participant ON chat.id = participant.chat_id
INNER JOIN participant T5 ON chat.id = T5.chat_id
WHERE participant.receiver_id = user_b AND participant.sender_id = user_a
AND T5.receiver_id = user_a AND T5.sender_id = user_b
It will thus return all the Chat objects for which two such Participant objects exist.
The above is not ideal however, since we make two JOINs. In case there is a unique_together constraint on the participants, as in:
class Participant(models.Model):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE, related_name='participants')
sender = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
receiver = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
class Meta:
unique_together = ['sender', 'receiver']
We can just count the number of Participant objects, like:
from django.db.models import Count, Q
Chat.objects.filter(
Q(participants__sender=user_a, participants__receiver=user_b) |
Q(participants__sender=user_b, participants__receiver=user_a)
).annotate(
nparticipants=Count('participants')
).get(
nparticipants=2
)
This will use the following query:
SELECT chat.id, chat.created, COUNT(participant.id) AS nparticipants
FROM chat
INNER JOIN participant ON chat.id = participant.chat_id
WHERE (participant.receiver_id = user_b AND participant.sender_id = user_a)
OR (participant.receiver_id = user_a AND participant.sender_id = user_b)
GROUP BY chat.id
HAVING COUNT(participant.id) = 2
We can use .get(..) here, since due to the unique_together constraint, it is guaranteed that there is at most one Chat object for which this will exist. We can thus then handle the situation with a Chat.DoesNotExist exception.
I am however not really convinced that the above modeling is ideal. First of all the number of records will scale quadratic with the number of participants: for three participants, there are six records. Furthermore a Chat is probably conceptually speaking not "directional": there is no sender and receiver, there are two or more peers that share information.

Django make a double foreign key lookup involving one to many to one relations

I have the following models:
def Order(models.Model):
receiver = models.ForeignKey(Receiver)
warehouse = models.ForeignKey(Warehouse)
def Receiver(models.Model):
user = models.ForeignKey(User) #this is not made one to one because user can have more than one receiver
name = ...
zipcode = ...
def Warehouse(models.Model):
city = ...
street = ...
I want to select all Warehouse entries related to request.User object. The only way i can do this now is:
orders = Order.objects.filter(receiver__user=request.User)
# here i set orders warehouse ids to list called ids
user_warehouses = Warehouse.objects.filter(pk__in=ids)
But i have a strong feeling that i am inventing the wheel. Is there a more simple Django-way of doing this?
Warehouse.objects.filter(order__receiver__user=request.user)
you can traverse relations backwards ("reverse lookup") and traverse multiple levels with the double-underscore syntax
https://docs.djangoproject.com/en/1.8/topics/db/queries/#lookups-that-span-relationships

exclude a query result in another query

Here i want to do is that ,i want to list all the person who didn't blocked me.Here in the table Blocked there is two columns name
who and whose . In whose column i store the id of the person whom i blocked and in the who column i store my id. Now i want to do that, when the blocked person click on
view-person button in my web page he cannot see profile of the person one who blocked him.
when i did this query blocked_list = Blocked.objects.filter(whose = user_id). Now i got the list of the persons who blocked me. Now i want to exclude all this person from this query total_profiles = persons.objects.all().exclude(blocked_list). How can i do this.
models.py
class persons(models.Model):
full_name = models.CharField(max_length=200)
class blocked(models.Model):
who = models.ForeignKey(persons)
whose = models.IntegerField(null=True)
views.py
def blocked(request):
blocked_list = Blocked.objects.filter(whose = user_id)
total_profiles = persons.objects.all().exclude(blocked_list)
return render_to_response('profiles/view_all.html', {'total_profiles':total_profiles,'}, context_instance=RequestContext(request),)
please correct the question if it is not correct.
You can try this:
total_profiles = persons.objects.all().exclude(id__in = blocked_list.values_list('id', flat=True))
It's untested, but adapted from this answer.
Some notes:
if persons has the default manager, you can omit all().
whose does not have an index, so it will become slow when your dataset gets big. You can use a ForeignKey field instead of an IntegerField
the common convention is to capitalize class names and to write model names in singular i.e. Person instead of persons

Django query across foreign keys

I need to get a list of users who have been referenced as a ForeignKey in two other models. I'm still a little unclear on queries when they reach this complexity.
Models:
class Page(models.Model):
user = models.ForeignKey(User)
class EmailSent(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=200)
In English, what I want to get is: 10 active users who have 0 pages and have never had an email with the name check_in sent to them.
Here's where I am:
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)[10]
but not sure how to do what is essentially:
email_sent = EmailSent.objects.filter(user=user, name='check_in')
Any ideas?
One possible way to get what you want is:
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)
EmailSent.objects.filter(user__in=users, name='check_in')[10]
Another way is,
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)
users.emailsent_set.all()[10]
Try the following:
users = User.objects.filter(is_active=1)\
.exclude(emailsent__name='check_in')\
.extra(select={'page_count': "select count(*) from YOURAPP_page where user_id = auth_user.id"}, where=['page_count 0'])[:10]

Fetching ManyToMany objects from multiple objects through intermediate tables

Is there an easy way to fetch the ManyToMany objects from a query that returns more than one object? The way I am doing it now doesn't feel as sexy as I would like it to. Here is how I am doing it now in my view:
contacts = Contact.objects.all()
# Use Custom Manager Method to Fetch Each Contacts Phone Numbers
contacts = PhoneNumber.objects.inject(contacts)
My Models:
class PhoneNumber(models.Model):
number = models.CharField()
type = models.CharField()
# My Custom Manager
objects = PhoneNumberManager()
class Contact(models.Model):
name = models.CharField()
numbers = models.ManyToManyField(PhoneNumber, through='ContactPhoneNumbers')
class ContactPhoneNumbers(models.Model):
number = models.ForeignKey(PhoneNumber)
contact = models.ForeignKey(Contact)
ext = models.CharField()
My Custom Manager:
class PhoneNumberManager(models.Manager):
def inject(self, contacts):
contact_ids = ','.join([str(item.id) for item in contacts])
cursor = connection.cursor()
cursor.execute("""
SELECT l.contact_id, l.ext, p.number, p.type
FROM svcontact_contactphonenumbers l, svcontact_phonenumber p
WHERE p.id = l.number_id AND l.contact_id IN(%s)
""" % contact_ids)
result = {}
for row in cursor.fetchall():
id = str(row[0])
if not id in result:
result[id] = []
result[id].append({
'ext': row[1],
'number': row[2],
'type': row[3]
})
for contact in contacts:
id = str(contact.id)
if id in result:
contact.phonenumbers = result[id]
return contacts
There are a couple things you can do to find sexiness here :-)
Django does not have any OOTB way to inject the properties of the through table into your Contact instance. A M2M table with extra data is a SQL concept, so Django wouldn't try to fight the relations, nor guess what should happen in the event of namespace collision, etc... . In fact, I'd go so far as to say that you probably do not want to inject arbitrary model properties onto your Contact object... if you find yourself needing to do that, then it's probably a sign you should revise your model definition.
Instead, Django provides convenient ways to access the relation seamlessly, both in queries and for data retrieval, all the while preserving the integrity of the entities. In this case, you'll find that your Contact object offers a contactphonenumbers_set property that you can use to access the through data:
>>> c = Contact.objects.get(id=1)
>>> c.contactphonenumbers_set.all()
# Would produce a list of ContactPhoneNumbers objects for that contact
This means, in your case, to iterate of all contact phone numbers (for example) you would:
for contact in Contact.objects.all():
for phone in contact.contactphonenumbers_set.all():
print phone.number.number, phone.number.type, phone.ext
If you really, really, really want to do the injection for some reason, you'll see you can do that using the 3-line code sample immediately above: just change the print statements into assignment statements.
On a separate note, just for future reference, you could have written your inject function without SQL statements. In Django, the through table is itself a model, so you can query it directly:
def inject(self, contacts):
contact_phone_numbers = ContactPhoneNumbers.objects.\
filter(contact__in=contacts)
# And then do the result construction...
# - use contact_phone_number.number.phone to get the phone and ext
# - use contact_phone_number.contact to get the contact instance