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?
Related
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.
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
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)
Django model
class Imones(models.Model): # Clients
id = models.AutoField(primary_key=True)
pavadinimas = models.CharField(unique=True, max_length=45) # name
class Priekabos(models.Model): # Products
id = models.AutoField(primary_key=True)
sutarties_nr = models.CharField(unique=True, max_length=45) # contract_number
vin = models.CharField(unique=True, max_length=45, verbose_name='VIN') # vehicle_identification_number
imones = models.ForeignKey(Imones, models.DO_NOTHING, verbose_name='Įmonė') # client_id
class Saskaitos(models.Model): # Invoices
id = models.AutoField(primary_key=True)
apmoketa = models.BooleanField(default=0) # Paid in full
imones = models.ForeignKey(Imones, models.DO_NOTHING) # client_id
MySQL query:
SELECT distinct sutarties_nr, vin, apmoketa
FROM priekabos pr
LEFT OUTER JOIN saskaitos sa
ON pr.imones_id = sa.imones_id
WHERE apmoketa=0 or apmoketa is null
Could someone explain how to translate MySQL query to Django query? I've tried:
obj = Saskaitos.objects.filter(apmoketa=0)
queryset = Priekabos.objects.filter(imones__in=obj.values('imones')).values()
But it doesn't return all values as expected. One more question - how to make Imones.pavadinimas appear in queryset results instead of plain id? Now it looks like that
I am using Django==1.8.7 and I have the following models
# a model in users.py
class User(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, blank=True)
displayname = models.CharField(max_length=100, blank=True)
# other fields deleted
# a model in healthrepo.py
class Report(models.Model):
id = models.AutoField(primary_key=True)
uploaded_by = models.ForeignKey(User, related_name='uploads',
db_index=True)
owner = models.ForeignKey(User, related_name='reports', db_index=True)
# other fields like dateofreport, deleted
I use the following Django queryset:
Report.objects.filter(owner__id=1).values('uploaded_by__username',
'uploaded_by__displayname').annotate(
total=Count('uploaded_by__username')
)
I see that this generates the following query:
SELECT T3."username", T3."displayname", COUNT(T3."username") AS "total" FROM "healthrepo_report"
INNER JOIN "users_user" T3 ON ( "healthrepo_report"."uploaded_by_id" = T3."id" )
WHERE "healthrepo_report”.”owner_id" = 1
GROUP BY T3."username", T3."displayname", "healthrepo_report"."dateofreport", "healthrepo_report”.”owner_id", "healthrepo_report"."uploaded_by_id"
ORDER BY "healthrepo_report"."dateofreport" DESC, "healthrepo_report"."user_id" ASC, "healthrepo_report"."uploaded_by_id" ASC
However, what I really wanted was just grouping based on "healthrepo_report”.”owner_id" and not multiple fields. i.e. What I wanted was:
SELECT T3."username", T3."displayname", COUNT(T3."username") AS "total" FROM "healthrepo_report"
INNER JOIN "users_user" T3 ON ( "healthrepo_report"."uploaded_by_id" = T3."id" )
WHERE "healthrepo_report”.”owner_id" = 1
GROUP BY T3."username", T3."displayname" ORDER BY "healthrepo_report"."dateofreport" DESC, "healthrepo_report"."user_id" ASC, "healthrepo_report"."uploaded_by_id" ASC
I am wondering why this is happening and how do I get grouping based on single column.
I just saw this post:
Django annotate and values(): extra field in 'group by' causes unexpected results
Changing the query by adding empty order_by() fixes it
Report.objects.filter(owner__id=1).values('uploaded_by__username',
'uploaded_by__displayname').annotate(
total=Count('uploaded_by__username')
).order_by()