Django query objects using non related models - django

I have these 3 models:
class Person(models.Model):
name
age
class Person_Address(models.Model):
address
number
city
state
person = models.ForeignKey(Person)
class State_Schedules(models.Model):
state
hour
I also have a models.QuerySet for the State_Schedules for filtering based on current time (basically the contactables() below).
How can I get the Person objects comparing it's state in django?
I tried this but it seems cumbersome having to fetch the Persons using their Person_Address.
persons = Person_Address.objects.select_related('person').filter(
state__in=(
_.state for _ in State_Schedules.objects.contactables()
)
)
If I could access the Person_Address association as a keyword to the Person's filter, this should be pretty easy, but I haven't found how to do so.
Is there a better approach or I'd have to resort to raw sql?

There is a foreign key from Person_Address to Person, so you can filter on the reverse relationship. Use double underscore notation to filter on fields in the Person_Address model.
states = [_.state for _ in State_Schedules.objects.contactables()]
people = Person.objects.filter(person_address__state__in=states)

Related

How to join multiple model using select_related?

i have few models namely
class Alpha(models.Model):
name = models.CharField()
class XXX(models.Model):
owner = models.ForeignKey(Alpha)
class YYY(models.Model):
name = models.OneToOneField(Alpha)
Now while doing select_related like this
test = Alpha.objects.filter(id=pk).select_related('XXX')
It gives me Invalid field name(s) given in select_related, choices are YYY
I understand that YYY is at OneToOne, so its showing up - but is there a way to fetch XXX also ? or should i use "prefetch_related". But i dont wanna use prefetch as its just making slow queries and meanwhile i have 7 models which needs to be select_related :(
You can use prefetch_related in order to achieve this. Also, add related_name in foreign key fields for referencing.
class Alpha(models.Model):
name = models.CharField()
class XXX(models.Model):
owner = models.ForeignKey(Alpha, related_name='xxx')
class YYY(models.Model):
name = models.OneToOneField(Alpha, related_name='yyy')
Alpha.objects.filter(id=pk).prefetch_related('xxx', 'yyy')
You can use select_related when going in the direction of the 1 model.
For example XXX.objects.all().select_related('alpha')
But when you go from 1 towards many you have to use prefetch_related.
so Alpha.objects.all().prefetch_related('xxx')
Check out this article

How to set the range of the primary-key of django objects based on the foreign key range and is it advisable?

I have the below models:
class Author(model):
name = CharField(...)
class Post(mode):
author = ForeignKey(Author)
title = CharField(...)
Suppose we have at most 100 authors. So the primary key of the autors would be in the range of 1 to 100
I want the primary keys of the post model be based on the primary key of the author of the post.
I mean, if the author's primary key is 34, then his/her posts primary keys be 34000001, 34000002, 34000003
is advisable to do this and how can I do it?
This is generally not advisable and might tempt you to follow (or invent) antipatterns. If your goal is to be able to easily access a particular author's posts, for example, you would be safest using Django's normal ORM patterns. For example:
# models.py
class Author(model):
name = CharField(...)
class Post(model):
author = ForeignKey(Author, related_name='posts')
title = CharField(...)
Then anywhere you want, you can...
sally = Author.objects.get(id=1)
sallys_posts = author.posts.all()
This would be much safer than things you might otherwise be tempted to do such as Post.objects.filter(pk__startswith=sally.pk) which would be something that I think would lead to a large number of bugs down the line, and would also mean that you miss out on a lot of normal pattern Django ORM benefits.

Django - Optimize queries with select_related()

I have the following model.
class Car(models.Model):
owner = models.ForeignKey('Driver')
class Country(models.Model)
name = models.CharField(max_length=255)
class Driver(models.Model):
name = models.CharField(max_length=255)
age = models.IntegerField()
country = models.ForeignKey('Country')
I want to select the name of drivers owning a car.
Car.objects.all().values('owner__name')
Do I need to use select_related() method, to avoid a join for each object, or it is redundant because implicit with values() method?
Car.objects.all().select_related('owner').values('owner__name')
In the same way, I want, this time, the name of countries with drivers owning a car. Which one is the best?
Car.objects.all().values('owner__country__name')
Car.objects.all().select_related('owner', 'country').values('owner__country__name')
Car.objects.all().select_related('owner__country').values('owner__country__name')
First, all the occurrences of .all() in your examples can be deleted; the manager (.objects) already has almost all methods of the QuerySet, except for .delete().
.select_related is only helpful when your eventual query returns model instances; then, all the foreign key of each instance will be pre-loaded.
But if you are using .values you are getting dictionaries, and there are no foreign key attributes to pre-load. So it should not be used in that case.
When you do .values('owner__name') Django already sees that it needs to join owners and cars, no extra queries are done.
In the last one you want Countries, so use Country.objects:
Country.objects.filter(driver__car__isnull=False).values('name')

Many-to-many "by proxy" relathionship in django

My data model consists of three main entities:
class User(models.Model):
...
class Source(models.Model):
user = models.ForeignKey(User, related_name='iuser')
country = models.ForeignKey(Country, on_delete=models.DO_NOTHING)
description = models.CharField(max_length=100)
class Destination(models.Model):
user = models.ForeignKey(User, related_name='wuser')
country = models.ForeignKey(Country)
I am trying to create a queryset which is join all sources with destinations by user (many to many). In such a way I would have a table with all possible source/destination combinations for every user.
In SQL I would simple JOIN the three tables and select the appropriate information from each table.
My question is how to perform the query? How to access the query data?
In django queries are done on the model object, its well documented. The queries or querysets are lazy and when they execute they generally return a list of dict, each dict in the list contains the field followed by the value eg: [{'user':'albert','country':'US and A :) ','description':'my description'},....].
All possible source,destination combinations for every user?
I think you will have to use a reverse relation ship to get this done eg:
my_joined_query = User.objects.values('user','source__country','source__description','destination__country')
notice that i'm using the smaller case name of the models Source and Destination which have ForeignKey relationship with User this will join all the three tabels go through the documentation its rich.
Edit:
To make an inner join you will have to tell the query, this can be simply achieved by using __isnull=False on the reverse model name:
my_innerjoined_query = User.objects.filter(source__isnull=False,destination__isnull=False)
This should do a inner join on all the tables.
Then you can select what you want to display by using values as earlier.
hope that helps. :)

Django ORM: count a subset of related items

I am looking to find a way to annotate a queryset with the counts of a subset of related items. Below is a subset of my models:
class Person(models.Model):
Name = models.CharField(max_length = 255)
PracticeAttended = models.ManyToManyField('Practice',
through = 'PracticeRecord')
class Club(models.Model):
Name = models.CharField(max_length = 255)
Slug = models.SlugField()
Members = models.ManyToManyField('Person')
class PracticeRecord(PersonRecord):
Person = models.ForeignKey(Person)
Practice = models.ForeignKey(Practice)
class Practice(models.Model):
Club = models.ForeignKey(Club, default = None, null = True)
Date = models.DateField()
I'm looking to make a queryset which annotates the number of club specific practices attended by a person. I can already find the total number of practices by that person with a query of Person.objects.all().annotate(Count('PracticeRecord'))
However I would like someway to annotate the number of practices that a person attends for a specific club.
I would prefer something using the django ORM without having to resort to writing raw SQL.
Thanks.
However I would like someway to annotate the number of practices that a person attends for a specific club.
Let us see.
First, find the specific club.
club = Club.objects.get(**conditions)
Next, filter all Persons who have practiced at this club.
persons = Person.objects.filter(practicerecord__Practice__Club = club)
Now, annotate with the count.
q = persons.annotate(count = Count('practicerecord'))
Edit
I was able to successfully make this work in my test setup: Django 1.2.3, Python 2.6.4, Postgresql 8.4, Ubuntu Karmic.
PS: It is a Good Idea™ to use lower case names for the fields. This makes it far easier to use the double underscore (__) syntax to chain fields. For e.g. in your case Django automatically creates practicerecord for each Person. When you try to access other fields of PracticeRecord through this field you have to remember to use title case.
If you had used lower case names, you could have written:
persons = Person.objects.filter(practicerecord__practice__club = club)
# ^^ ^^
which looks far more uniform.
PPS: It is Count('practicerecord') (note the lower case).
I'm afraid that raw sql is the only option here. Anyway it's not that scary and hard to manage if you put it to model manager.