Django - annotate on multiple fields and load relevant objects - django

Let's say I have the following models
class Author(models.Model):
name = models.CharField(max_length=25)
...
class Publisher(models.Model):
name = models.CharField(max_length=25)
...
class Book(models.Model):
author = models.ForeignKey(Author)
publisher = models.ForeignKey(Publisher)
...
For some reason, I want to query books and group results by the author and publisher, so:
books = Book.objects.values('author', 'publisher').annotate('sth'=Avg('sth_else'))
with the results looking like:
<BookQuerySet [{'author': 2, 'publisher': 1, 'sth': 1.0}]>
Is it possible to load the whole Author and Publisher objects and not just their related ids?

I am afraid it isn't. In order to get the respective author and publisher objects. you might have to just use the ids you got here from the query and make another query.
authors = Author.objects.filter(id__in = authors)
publishers = Publisher.objects.filter(id__in = publishers)
where authors,publishers is the list of ids you got above

Related

use manytomany field in both the models Django

I have two models named Profile and Controversy. My requirement is many people can be involved in a controversy and a single person can have multiple Controversies. With that said, I feel like I should a ManyToMany field in both the models but I'm guessing that violates ManyToMany fields documentation as it should be used in only model.
my models are as follows :
class Profile(models.model):
name = models.CharField(max_length=200)
Controversy = models.ManyToManyField(Controversy) # As one person can have multiple controveries
class Controversy(models.Model):
year = models.Datefield()
other_people_involved = models.ManytoManyField(profile) # As multiple people can be involved in a controversy
description = models.TextField()
This obviously will throw error.
I'm not able to understand as to how to tackle such a scenario
You can try this way:
Create another model to store the person and controvery connection.
class Profile(models.model):
name = models.CharField(max_length=200)
class Controversy(models.Model):
year = models.Datefield()
description = models.TextField()
class PeopleInvolved(models.Model):
controversy = models.ManyToManyField(Controversy)
person = models.ManytoManyField(profile)
So to list the controversies of a person do this:
controversies = [i.controversy for i in PeopleInvolved.objects.filter(person=[profile_id])] #pass the profile id of the person.
And to list the people involved in a controversy do this:
peoples = [i.person for i in PeopleInvolved.objects.filter(controversy=[controversy_id])] #pass the controversy id.

How to filter joined models in Django?

I have the following models:
class Street(models.Model):
name = models.CharField(max_length=40)
class House(models.Model):
street = models.ForeignKey(Street, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('street', 'number')
class Room(models.Model):
house = models.ForeignKey(House, models.PROTECT)
number = models.CharField(max_length=10)
class Meta:
unique_together = ('house', 'number')
I already know the ID of a Street and would like to get all the rooms of all the houses in that street. In SQL that is easy:
SELECT *
FROM room JOIN house ON house.id = room.house_id
WHERE house.street_id = xyz;
Now how do I do this in Django? I tried
Room.objects.select_related('house').filter(street=xyz)
But I get an exception saying I can't access this field:
django.core.exceptions.FieldError: Cannot resolve keyword 'street' into field. Choices are: house, house_id, id, number
Because of the amounts of data I am facing, I would really like to be able to join and filter using a single query! When giving up one or the other, I would have to resort to either making multiple queries or filtering in Python, both of which are inherently inefficient. An alternative would be raw queries, I guess...
You can get access to related object's field with __ syntax:
Room.objects.select_related('house').filter(house__street=xyz)
This can be done as much time as you need, to select rooms by street name you can do this:
Room.objects.select_related('house').filter(house__street__name=xyz)
Chech details here.

Django-views Custom filter

If having different books stored in django database, each book has a date in which it was added to the database. Is their a way of filtering books written by a certain author that was within a date range only using django views?
Not sure what you mean by only django views, I assume you want to use querysets. Your question is poorly written - read this.
class Book(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField()
author = models.ForeignKey(Author)
class Author(models.Model):
name = models.CharField(max_length=200)
And Queryset would be something like this.
books = Book.objects.filter(author__name=authors_name,
date__range=["2011-01-01", "2011-01-31"])

django: Count objects with the same value

Lets say I have following models:
class Book(models.Model):
name = models.CharField(max_length=70)
lang = models.CharField(max_length=70)
author = models.FK(Author)
class Author(models.Model):
name = models.CharField(max_length=70)
And I want to write something to get a list of authors with annotated field which shows amount of books in each language. Can't imagine an annotation for it :(, e.g. {'en': 10, 'ru': 1...etc}
e.g. just counts all, Author.objects.annotate(languages=Count(book__lang))
Simple annotation should help you:
Book.objects.values('lang').annotate(lang=Count('author')).order_by('lang')

Defining a two way many to many in Django

Just starting in Python/Django framework so sorry if this is dumb... but i cant find any solution.
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField(Contact)
class Contact(models.Model):
name = models.CharField(max_length=200)
dealers = models.ManyToManyField(Dealer)
I have this relation set up however when I run SyncDB it doesnt work. It tells me that Contact is not defined on this line
contacts = models.ManyToManyField(Contact)
Im more familiar with compiled languages. Is there anyway to tell python that the contact class exists, or better yet is there a special syntax im missing for defining this kind of relation.
I dont see a need for a two way ManyToMany in both the models, as they are a M:N relationship (2 way relationship).
Your issue here is, Contact is not defined at the point of execution of this code:
contacts = models.ManyToManyField(Contact)
So You need to wrap it in quotes for it to work
contacts = models.ManyToManyField('Contact')
Documentation on that can be found here
I would recommend the following models:
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField('Contact')
class Contact(models.Model):
name = models.CharField(max_length=200)
and It would do exactly what you are looking for.
You can read about ManyToMany relationships here. The same link also covers how to handle Reverse m2m queries
If you want to do a two way ManyToMany both, you just only need to do this:
class Dealer(models.Model):
name = models.CharField(max_length=200)
contacts = models.ManyToManyField('Contact', blank=True)
class Contact(models.Model):
name = models.CharField(max_length=200)
dealers = models.ManyToManyField('Dealer', through=Dealer.projects.through, blank=True)
I guess it will work to you.