Django models queries - python

I have 3 tables person(id, email,password,type), user_location(id,location,u_id) and reviews(id,review,from_id,to_id). The user_location(u_id) is the foreignkey to person(id). The review(from_id,to_id) is also foreignkey to person(id). So how can i filter out a person with type 'a' and location 'b' and the reviews he got with the reviewers name?
models.py
class Person(models.Model):
email = models.CharField(max_length=30)
pwd = models.CharField(max_length=30)
type = models.CharField(max_length=30)
class User_locations(models.Model):
location = models.CharField(max_length=30)
u_id = models.ForeignKey('Person', on_delete=models.CASCADE)

Not sure if I really understand what you're trying to do but this should point you in the right direction. Don't forget to refer to the doc for Many-to-one relationships and Lookups that span relationships. It says there:
To refer to a “reverse” relationship, use the lowercase name of the model.
And then use the normal __ to access attributes.
a_and_b = Person.objects.filter(type='a', user_locations__location='b')
reviewers = Person.objects.filter(to_id__in=a_and_b)
The first query selects all users with type a and location b. The second query filters on those results. This is all untested so you might need to tweak a little. Bottom line: follow the relationships. As a side note, you might want to read up on Django model naming conventions.

Related

Look up objects that aren't the target of any foreign keys using Django ORM

I have two models, Author and Book. I need to get all Authors who have no Books with Django's ORM. How can I do that?
Here are my models:
class Author(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
class Book(models.Model):
name = models.CharField(max_length=500)
author = models.ForeignKey(Author, related_name='books')
I am currently doing it with for loop, but I would prefer to do it with the ORM:
for person in Author.objects.all():
if person.books.count() == 0:
"something code"
But it need to make update for objects.
I tried this but I just get an empty QuerySet:
Author.objects.select_related('books').values('id').annotate(books_count=Count('id')).filter(books_count=0)
.annotate(books_count=Count('id'))
Django will count the Author's ids here. Try
.annotate(books_count=Count('books__id'))
instead. The double underscore indicates a query that spans relationships:
Django offers a powerful and intuitive way to "follow" relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.
This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
>>> Entry.objects.filter(blog__name='Beatles Blog')

What's the use of Intermediate models in Django?

Why do we use an intermediate model?
Can't we just use Many to many relationship without intermediate model?
M2M relationships require intermediate tables. You can read more about what M2M relationships are and why they require an intermediate table (referred to as a junction table in the article) here:
Django abstracts this away by automagically creating this intermediate table for you, unless you need to add custom fields on it. If you do, then you can define it by overriding the through parameter as shown here
Here's a quick picture of why the table is required
Source: https://www.geeksforgeeks.org/intermediate-fields-in-django-python/
Let's say you have two models which have a Many-to-Many relationship, like Customer and Product. One customer can buy many products and a product can be bought by many customers.
But you can have some data that doesn't belong to neither of them, but are important to the transaction, like: quantity or date.
Quantity and date are the intermediary data which are stored in intermediary models.
from django.db import models
class Item(models.Model):
name = models.CharField(max_length = 128)
price = models.DecimalField(max_digits = 5, decimal_places = 2)
def __str__(self):
return self.name
class Customer(models.Model):
name = models.CharField(max_length = 128)
age = models.IntegerField()
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
def __str__(self):
return self.name
class Purchase(models.Model):
item = models.ForeignKey(Item, on_delete = models.CASCADE)
customer = models.ForeignKey(Customer, on_delete = models.CASCADE)
date_purchased = models.DateField()
quantity_purchased = models.IntegerField()
When you buy a product, you do it through the Purchase model: the client customer buys quantity_purchased quantity of items item in date_purchased.
The Purchase model is the Intermediate model.
Django documentation says:
...if you want to manually specify the intermediary table, you can use
the through option to specify the Django model that represents the
intermediate table that you want to use.
In this case we have this line in the Customer model, which defines the intermediary model in through = 'Purchase'
items_purchased = models.ManyToManyField(Item, through = 'Purchase')
Let's now use the example from the Django Documentation.
You have a database of musicians with a Many-to-Many relationship with the bands the belong to: a musician can belong can be part of many bands, and the bands can have many musicians.
What data do you want to keep?
For musicians (person): name and instrument they play
For the bands: name and style.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
But, wouldn't you think that knowing when the person joined the band is important? What model would be the natural place to add a date_joined field? It makes no sense to add it to Person or Group, because it's not an intrinsic field for each of them, but it's related to an action: joining the band.
So you make a small, but important adjustment. You create an intermediate model that will relate the Person, the Group with the Membership status (which includes the date_joined).
The new version is like this:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
age = models.IntegerField()
class Group(models.Model):
name = models.CharField(max_length=128)
style = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
The changes are:
You added a new class called Membership which reflects the membership status.
In the Group model you added members = models.ManyToManyField(Person, through='Membership'). With this you relate Person and Group with Membership, thanks to through.
Something important to clarify.
An intermediate model, or in relational database terms, an associative entity, are always needed in a Many-to-Many (M2M) relationship.
A relational database requires the implementation of a base relation
(or base table) to resolve many-to-many relationships. A base relation
representing this kind of entity is called, informally, an associative
table... that can contain references to columns from the same or different database tables within the same database.
An associative (or junction) table maps two or more tables together by
referencing the primary keys of each data table. In effect, it
contains a number of foreign keys, each in a many-to-one relationship
from the junction table to the individual data tables. The PK of the
associative table is typically composed of the FK columns themselves. (source)
Django will create the intermediate model, even when you don't explicitly define it with through.
Behind the scenes, Django creates an intermediary join table to
represent the many-to-many relationship. By default, this table name
is generated using the name of the many-to-many field and the name of
the table for the model that contains it.
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.(source)

Django OneToOneField and ForeignKeyField add "_id" suffix to the field

When I checked group_cover table which is created by Django, there were group_id_id field and group_cover field.
I'd like to change group_id_id to group_id.
models.py
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group_id = models.OneToOneField(Group, primary_key=True) # this create group_id_id
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user_id = models.ForeignKey(User2) # this create user_id_id
group_id = models.ForeignKey(Group) # this create group_id_id
Yeah, if I write,
group = models.OneToOneField(Group, primary_key=True)
It might work, but I may not need "_id" suffix on some field.
I read this document, but owing to my poor English, I couldn't understand the way.
Would you please teach me how to change?
Django adds an _id postix to primary keys that are generated automatically. You generally don't need to worry about them unless using a legacy data base.
Solution 2 would be the one i would recommend for a new project. Solution 1 for legacy databases.
Solution 1
To modify your existing code, use the following db_column attribute as it allows you to name the field in the database.:
group = models.AutoField(primary_key=True, db_column='group_id')
Documentation
Solution 2
To get the same results in a more "Django" way let Django generate the Primary keys automatically then reference the model in the OneToOne and Foreign key fields as shown below.
class Group(models.Model):
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group = models.OneToOneField(Group)
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user = models.ForeignKey(User2)
group = models.ForeignKey(Group)
Your assumption is correct, you need to rename your fields to not include the _id (i.e group instead of group_id). This will fix your "issue" but more than anything it more accurately represents the relationship/field. You have relationships to a model, not a reference to the id.
_id is an automatic reference provided by django to make it easier to just retrieve the _id from a model.
From the documentation
Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You’ll always deal with the field names of your model object.
You should not worry about _id that is being added in database table. You should not deal with database if you are using ORM in Django. Also, you do not need to specify id unless its special type - group of attributes.
I would do it like this (I believe you do not need that many classes):
class Group(models.Model):
name = models.CharField(max_length=50, unique=False, blank=False)
cover = models.ImageField(upload_to="/image/group/")
users = models.ManyToManyField(User2)
Then you should access attributes with object notation. If you want id, use group.id, if you want to filter object, use Group.objects.filter(id__gt=10) or Group.objects.get(id=1) etc. My model should be doing exactly what you want to achieve.

Django models with same or similar fields

I started to learn django and already have one question:
what kind of model creation are better if tables have similar fields?
1) Standart way form official tutorial like
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
As you see both models have same name and address fields. I found in https://docs.djangoproject.com/en/1.6/topics/db/models/#multi-table-inheritance the Multi-table ingeritance which works like this:
2)
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
As doc says: All of the fields of Place will also be available in Restaurant, although the data will reside in a different database table.
Well, is there any principal differences beteeen this two? And which one shall I use?
In this case, I think you should use inheritance (solution 2).
Because the Restaurant is a special Place, which means Restaurant have field name and address, additionally, Restaurant have field serves_hot_dogs and serves_pizza.
Considering the above description, all solution is reasonable. But, Restaurant is a Place, Restaurant must can be processed as Place.
In solution1, Restaurant and Place are separated, you cannot process Restaurant as Place.(Just considering OOP)
In other side, in solution1, if I save a Place with address A, and I add a Restaurant with same address. I save same address twice in database, it leads to redundant and inconsistency.
This question is similar with the different of Composition and Inheritance in OOP.
Yes, there is a difference and you've just pasted link to documentation, which explains this difference.
Restaurant db table in (2) consists of two extra fields (serves_hot_dogs and serves_pizza) and a ForeignKey to record in Place db table.
As per the question which model you should use -- whatever suits you best. I usually use multi table inheritance in similar cases.
Before jumping into table inheritance please see: https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager. I use it to limit the number of queries when dealing with multi table inheritance.

How to perform queries in Django following double-join relationships (or: How to get around Django's restrictions on ManyToMany "through" models?)

There must be a way to do this query through the ORM, but I'm not seeing it.
The Setup
Here's what I'm modelling: one Tenant can occupy multiple rooms and one User can own multiple rooms. So Rooms have an FK to Tenant and an FK to User. Rooms are also maintained by a (possibly distinct) User.
That is, I have these (simplified) models:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User)
maintainer = models.ForeignKey(User)
tenant = models.ForeignKey(Tenant)
The Problem
Given a Tenant, I want the Users owning a room which they occupy.
The relevant SQL query would be:
SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;
Getting any individual value off the related User objects can be done with, for example, my_tenant.rooms.values_list('owner__email', flat=True), but getting a full queryset of Users is tripping me up.
Normally one way to solve it would be to set up a ManyToMany field on my Tenant model pointing at User with TenantRoom as the 'through' model. That won't work in this case, though, because the TenantRoom model has a second (unrelated) ForeignKey to User(see "restictions"). Plus it seems like needless clutter on the Tenant model.
Doing my_tenant.rooms.values_list('user', flat=True) gets me close, but returns a ValuesListQuerySet of user IDs rather than a queryset of the actual User objects.
The Question
So: is there a way to get a queryset of the actual model instances, through the ORM, using just one query?
Edit
If there is, in fact, no way to do this directly in one query through the ORM, what is the best (some combination of most performant, most idiomatic, most readable, etc.) way to accomplish what I'm looking for? Here are the options I see:
Subselect
users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
Subselect through Python (see Performance considerations for reasoning behind this)
user_ids = id__in=my_tenant.rooms.values_list('user')
users = User.objects.filter(id__in=list(user_ids))
Raw SQL:
User.objects.all("""SELECT auth_user.*
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id""")
Others...?
The proper way to do this is with related_name:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User, related_name='owns')
maintainer = models.ForeignKey(User, related_name='maintains')
tenant = models.ForeignKey(Tenant)
Then you can do this:
jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)
User.objects.filter(owns__tenant=bob)

Categories

Resources