How to inner join 4 tables in Django? - python

We want to retrieve all table records at a time how to implement like SQL queries in django orm.
Example SQL query:
select *
from Company_info
inner join Bank_info on Bank_info.manufacturer = Company_info.manufacturer
inner join Company_info on Company_info.manufacturer = Company_info.manufacturer
inner join Transport_info on Transport_info.manufacturer = Company_info.manufacturer
Code:
class Manufacturer(models.Model):
name = models.CharField(max_length=42)
class Bank_info(models.Model):
account = models.CharField(max_length=42)
manufacturer = models.ForeignKey(Manufacturer, on_delete= models.CASCADE)
class Company_info(models.Model):
name= models.CharField(max_length=42)
manufacturer = models.ForeignKey(Manufacturer, on_delete= models.CASCADE)
class Transport_info(models.Model):
name= models.CharField(max_length=42)
manufacturer = models.ForeignKey(Manufacturer, on_delete= models.CASCADE)

You can use prefetch_related for this, as mentioned in comments:
Manufacturer.objects.all().prefetch_related('related_name2', 'related_name3', 'related_name4')
This, in fact, will not perform an SQL INNER JOIN, but will join prefetched objects on Python level - this will decrease hits to DB when using related object fields.

Related

How to join tables by list of ids in Django ORM

I have two tables - Players and Buildings. For example:
class Players(models.Model):
username = models.CharField(max_length=500)
buildings = models.CharField(validators=[int_list_validator], default=list, max_length=100)
class Buildings(models.Model):
building_name = models.CharField(max_length=500)
Player can have a lot of buildings, there are in the Player.buildings field as list of ids.
I need to write a query to get building information for the player. In Postgres SQL, I got that query:
SELECT *
FROM players p
LEFT JOIN buildings b on b.id=ANY(buildings) where p.id = %s;
But how I can write that SQL query, using Python and Django ORM?
You should use singular names for your models.
class Player(models.Model):
username = models.CharField(max_length=500)
buildings = models.ManyToManyField(Building)
class Building(models.Model):
building_name = models.CharField(max_length=500)
Now when you want to access the set of buildings for your player you can do the following:
Player.objects.get("pk of player").buildings.all()

Multiple JOIN using Django ORM

These are my 2 Django models:
class Brand(models.Model):
brand_no = models.CharField(max_length=100, unique = True)
class Meta:
db_table = 'brand'
class BrandWhereUsed(models.Model): # Brand is a component of parent brand
brand = models.ForeignKey(Brand, related_name="where_used", on_delete=models.CASCADE)
parent_brand = models.ForeignKey(Brand, on_delete=models.PROTECT)
class Meta:
constraints = [
models.UniqueConstraint(fields=['brand', 'parent_brand '], name='brand_parent')
]
db_table = 'brand_where_used'
And I would like to do convert this SQL query to Django ORM, not raw query:
SELECT t1.id, t1.brand_no
, GROUP_CONCAT(t2.parent_brand_id) AS where_used
, GROUP_CONCAT(t3.item_no) AS where_used_brand_no
FROM brand AS t1
LEFT OUTER JOIN brand_where_used AS t2 ON (t2.item_id = t1.id)
LEFT OUTER JOIN brand AS t3 ON (t3.id = t2.parent_brand_id)
GROUP BY t1.id
I tried this thread :GROUP_CONCAT equivalent in Django .
But seems like it generated incorrected result query.
Could you please help me on this?

ORM Query with multiple LEFT joins

Toy example: Let's say I have the following models:
# Person ---lives_in--> City ---part_of---> State
class Person(models.Model)
name = models.CharField(max_length=100)
lives_in = models.ForeignKey('City', on_delete=models.CASCADE)
class City(models.Model)
name = models.CharField(max_length=100)
part_of = models.ForeignKey('State', on_delete=models.CASCADE)
class State(models.Model):
name = models.CharField(max_length=100)
How do I get a list of people who live in a particular state using Django ORM?
In regular SQL, it be something like
SELECT p.*
FROM person p
LEFT JOIN city c ON (p.lives_in = c.id)
LEFT JOIN state s ON (c.part_of = s.id)
WHERE c.name = 'MA'
You could simply traverse through the relationships using __ notation.
people_in_ma = Person.objects.filter(lives_in__part_of__name="MA")
https://docs.djangoproject.com/en/2.2/topics/db/queries/#lookups-that-span-relationships

Django join tables with ORM and conditional Where clause

I have 4 tables to join; Personnels,Machines and Locations. I want to join these tables and add where clause to end of the ORM query if request body includes filtering data. Here is my models and raw query (I want to write this query in django ORM) and sample if condition for where clause;
Models ;
class Sales(models.Model):
MachineId = models.ForeignKey(Machines,on_delete=models.CASCADE,db_column='MachineId',related_name='%(class)s_Machine')
PersonnelId = models.ForeignKey(Personnels,on_delete=models.CASCADE,db_column='PersonnelId',related_name='%(class)s_Personnel')
LocationId = models.ForeignKey(Locations,on_delete=models.CASCADE,db_column='LocationId',related_name='%(class)s_Location')
class Meta:
db_table = "Sales"
class Machines(models.Model):
Name = models.CharField(max_length=200)
Fee = models.DecimalField(max_digits=10,decimal_places=3)
class Meta:
db_table = "Machines"
class Personnels(models.Model):
name = models.CharField(max_length=200)
surname = models.CharField(max_length=200)
class Meta:
db_table = "Personnels"
class Locations(models.Model):
Latitude = models.FloatField()
Longitude = models.FloatField()
LocationName = models.CharField(max_length=1000)
class Meta:
db_table = "Locations"
As you see I have 4 models. "Sales" table has foreignkeys to others. I want to get all informations in tables with using these foreign keys.(With Inner Join)
query = '''select * from "Sales" as "SL" INNER JOIN "Personnels" as "PL" ON ("SL"."PersonnelId" = "PL"."user_id") INNER JOIN "Machines" as "MC" ON ("SL"."MachineId" = "MC"."id") INNER JOIN "Locations" as "LC" ON ("SL"."LocationId" = "LC"."id") '''
if request.method=='POST':
if request.data['personnel_name'] and request.data['personnel_name'] is not None:
personnel_name = request.data['personnel_name']
condition = '''WHERE "PL"."name" = '{0}' '''.format(personnel_name)
query = query+condition
As it is seen, there are lots of quotes (if I don't write,postgresql makes some trouble) and code is not clean.
My question is, how can I write this query with using django ORM? As you see, I want to add where conditions dynamically. How can I achieve that?
I'm going to use conventional naming, with only class names captilized, and model names singular.
class Sale(models.Model):
machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
db_column and db_table is useful if you have to connect the django app use an existing database. If not, django will create sensible table names by default. The table name can be different from the model field name.
To create a join where, use a queryset filter.
Sale.objects.filter(person__name='Jane Janes')
You might not need more joins, since django will perform additional queries when needed, but it can be achieved using select_related, and can give you better performance, since it reduces the total number of sql queries needed.
Sale.objects.filter(person__name='Jane Janes').select_related('machine', 'person', 'location')
It can be useful to inspect the actual SQL that will be performed when you evalute a queryset. You can do this by accessing the QuerySet.query property.
queryset = Sale.objects.select_related('machine').filter(
person__name='Jim', location__name='London')
print(queryset.query)

Stringing together a series of foreign keys to return a table value in Django

How do I select the ctype knowing the user?
models.py:
from django.contrib.auth.models import User
class CompanyType(models.Model):
ctype = models.CharField(max_length=20)
class Company(models.Model):
name = models.CharField(max_length=50)
companytype = models.ForeignKey(CompanyType, on_delete=models.CASCADE)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
I know I can do it this way, but is there a way to chain it altogether in one line?
def getcompanytype(request):
user_id = request.user.id
company_id = Profile.objects.get(user_id__exact=user_id).company_id
companytype_id = Company.objects.get(id=company_id).companytype_id
companytype = CompanyType.objects.get(id=companytype_id).ctype
return(companytype)
essentially I want this SQL statement:
SELECT
ct.ctype
From auth_user u
left outer join Profile p
on p.user_id = u.id
left outer join Company c
on p.company_id = c.id
left outer join CompanyType ct
on ct.id = c.companytype_id
where u.id = 1 # actually current user ID, not hardcoded "1"
I think you can do this by following the reverse relationships of the ForeignKeys in a filter or get call, joining the different relations together with double underscores:
CompanyType.objects.get(company__profile__user__id__exact=user_id).ctype
Although I'd suggest you try that out in a Django shell and make sure that the reverse relationships are named how you'd expect! (For example, CompanyType will have a company_set attribute that gives all the companies with a particular CompanyType; the _set shouldn't be included when you do filter queries.)

Categories

Resources