Django not in other table (Not using id) - django

I have:
Models:
class Category(models.Model):
description = models.CharField(unique=True, max_length=200)
myfield = models.CharField(unique=True, max_length=200)
class Car(models.Model):
categorys = models.ManyToManyField(Category)
myfield = models.CharField(unique=True, max_length=200)
I am trying to execute the following query:
Car.objects.filter(category__id = c.id )
.exclude(myfield__in =
Category.objects.all()
.only('myfield')
)
.order_by("id")
.first()
I expected to find a result like this:
SELECT ...
FROM `myapp_car`
INNER JOIN `myapp_car_categorys`
ON (`myapp_car`.`id` = `myapp_car_categorys`.`car_id`)
WHERE ( ...
AND NOT (`myapp_car`.`myfield` IN
(SELECT `myapp_category`.`myfield` FROM `myapp_category`)))
ORDER BY `myapp_car`.`id` ASC LIMIT 1;
But i find:
SELECT ...
FROM `myapp_car`
INNER JOIN `myapp_car_categorys`
ON (`myapp_car`.`id` = `myapp_car_categorys`.`car_id`)
WHERE ( ...
AND NOT (`myapp_car`.`myfield` IN
(SELECT `myapp_category`.`id` FROM `myapp_category`)))
ORDER BY `myapp_car`.`id` ASC LIMIT 1;
I need use myfield in select, not id:
(SELECT `myapp_category`.`myfield` FROM `myapp_category`)
How would I get this result?

Even if you use only it will return the objects of Category model. So if you use it in in filtering the category objects will be used for filtering and for SQL the ids of those category objects.
If you need to filter on the myfield values on all the category objects, then use values instead:
....exclude(myfield__in=Category.objects.values('myfield'))

Related

Running aggregate function on Django queryset union with renamed fields raises django.db.utils.DatabaseError

I am using Django 3.2
I am trying to create a query that queries two different models and does the following:
renames the returned columns (so the queries can be 'union'ed together
'unions' the two querysets (after column rename using annotate)
tries to run an aggregate function Sum on the union (this is where it barfs).
This is a simplified version of my codebase:
Models
class EventCategory(models.Model):
name = models.CharField(max_length=16)
class Event(models.Model):
name = models.CharField(max_length=32)
category = models.ForeignKey(EventCategory, on_delete=models.CASCADE)
class Tournament(models.Model):
name = models.CharField(max_length=32)
category = models.ForeignKey(EventCategory, on_delete=models.CASCADE)
prize_money = models.IntegerField()
class TournamentAward(models.Model):
awardee = models.ForeignKey(setting.AUTH_USER_MODEL, on_delete=models.CASCADE)
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Game(models.Model):
player = models.ForeignKey(setting.AUTH_USER_MODEL, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
payment = models.SmallPositiveInteger()
created_at = models.DateTimeField(auto_now_add=True)
Queries
payment_earned_today = Game.objects.filter(
player=user,
created_at__year=year,
created_at__month=month,
created_at__day=day
).annotate(category=models.F('event__category'))\
.only('category','payment')
tournament_prize_today = TournamentAward.objects.filter(
awardee=user,
created_at__year=year,
created_at__month=month,
created_at__day=day
).select_related('tournament__category')\
.annotate(category=models.F('tournament__category'))\
.annotate(payment=models.F('tournament__prize_money'))\
.only('category','payment')
# Union the two querysets ...
union_queryset = payment_earned_today.union( tournament_prize_today )
results = union_queryset.aggregate(total=models.Sum('payment'))
On the line when I try to calculate the total, I get the following error:
django.db.utils.DatabaseError: ORDER BY not allowed in subqueries of compound statements
How can I union two models and calculate an aggregate function on the union?
at first:
you don't need to rename fields.
filter_query = Q(created_at__year=year,created_at__month=month,created_at__day=day)
payment_earned_today = Game.objects.filter(player=user, filter_query).values('event__category'. 'payment')
at second:
you can set order_by before aggregate.
queryset.query.order_by=None
at third:
Why you don't go from category?
event_query = Q(event__game__created_at__year=year, event__game__created_at__month=month, event__game__created_at__day=day, event__game__player=user)
tournament_query = Q(tournament__tournamentaward__created_at__year=year, tournament__tournamentaward__created_at__month=month, tournament__tournamentaward__created_at__day=day, tournament__tournamentaward__awardee=user)
all_category = EventCategory.ojects.filter(event_query | tournament_query)
I don't understand if is an error in your last core row or not:
results is plural, but you have aggregate(sum), it give you only one result. Therefore:
all_category_with_summ_in_day_for_user = all_category.annotate(Sum('tournament__prize_money'), Sum('event__game__payment'))
# or
summs_for_all_category_in_day_for_user= all_category.aggregate(Sum('tournament__prize_money'), Sum('event__game__payment'))

Django ORM: Reverse relation query multiple model from single models

I am in trouble querying reverse relation, I learned a lot about select_related and prefetch_related, yet I failed to achieve this.
At first see my models:
from django.db import models
import uuid
class Person(models.Model):
alias = models.UUIDField(primary_key=True,default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=20)
class Appointment(models.Model):
patient = models.ForeignKey(Person, related_name="patient_for_appointment", on_delete=models.CASCADE)
data = models.CharField(max_length=20)
class Sales(models.Model):
customer = models.ForeignKey(Person, related_name="customer_for_sales", on_delete=models.CASCADE)
amount = models.FloatField()
class Prescription(models.Model):
patient = models.ForeignKey(Person, related_name="Patient_for_prescription", on_delete=models.CASCADE)
details = models.CharField(max_length=100)
I am trying to filter Person Model to check if the person has any prescription, sales, and appointment I want to get these all with single query like the person. will filter it with a person alias (primary key)
I can filter it with separate query like
patient_alias = '53fsdfsdf-fdsfds-df-fdf'
queryset = Appointment.objects.filter(
patient__alias=patient_alias
)
But I don't want this, coz, it has a performance issue. I don't want this with separate query.
I want to query only Person model to check if a person has Appointment, prescription or Sales
like Person.objects.filter(alias='a person alias)
Can anyone please help me to achieve this?
Much appreciated
We can select all Persons, except the ones that hav no Appointment, nor a Sales or Prescription with:
Person.objects.exclude(
patient_for_appointment=None,
customer_for_sales=None,
Patient_for_prescription=None
)
This will yield a query that looks like:
SELECT person.alias, person.name
FROM person
WHERE NOT (
person.alias IN (
SELECT U0.alias
FROM person U0
LEFT OUTER JOIN prescription U1 ON U0.alias = U1.patient_id
WHERE U1.id IS NULL
)
AND person.alias IN (
SELECT U0.alias
FROM person U0
LEFT OUTER JOIN sales U1 ON U0.alias = U1.customer_id
WHERE U1.id IS NULL AND U0.alias = person.alias
)
AND person.alias IN (
SELECT U0.alias
FROM person U0
LEFT OUTER JOIN appointment U1
ON U0.alias = U1.patient_id
WHERE U1.id IS NULL AND U0.alias = person.alias
)
)
Or we can make a union, like:
Person.objects.filter(
patient_for_appointment__isnull=False
).union(
Person.objects.filter(customer_for_sales__isnull=False),
Person.objects.filter(Patient_for_prescription__isnull=False)
)
This will result in a query that looks like:
(
SELECT person.alias, person.name
FROM person
INNER JOIN appointment ON person.alias = appointment.patient_id
WHERE appointment.id IS NOT NULL
) UNION (
SELECT person.alias, person.name FROM person
INNER JOIN sales ON person.alias = sales.customer_id
WHERE sales.id IS NOT NULL
) UNION (
SELECT person.alias, person.name
FROM person
INNER JOIN prescription ON person.alias = prescription.patient_id
WHERE prescription.id IS NOT NULL
)

Django FULL OUTER JOIN

I have these three tables
class IdentificationAddress(models.Model):
id_ident_address = models.AutoField(primary_key=True)
ident = models.ForeignKey('Ident', models.DO_NOTHING, db_column='ident')
address = models.TextField()
time = models.DateTimeField()
class Meta:
managed = False
db_table = 'identification_address'
class IdentC(models.Model):
id_ident = models.AutoField(primary_key=True)
ident = models.TextField(unique=True)
name = models.TextField()
class Meta:
managed = False
db_table = 'ident_c'
class location(models.Model):
id_ident_loc = models.AutoField(primary_key=True)
ident = models.ForeignKey('IdentC', models.DO_NOTHING, db_column='ident')
loc_name = models.TextField()
class Meta:
managed = False
db_table = 'location
I want to get the last
address field (It could be zero) from IdentificationAddress model, the last _loc_name_ field (it matches at least one) from location model, name field (Only one) from IdentC model and ident field. The search is base on ident field.
I have been reading about many_to_many relationships and prefetch_related. But, they don't seem to be the best way to get these information.
If a use SQL syntax, this instruction does the job:
SELECT ident_c.name, ident_c.ident, identification_address.address, location.loc_name FROM identn_c FULL OUTER JOIN location ON ident_c.ident=location.ident FULL OUTER JOIN identification_address ON ident_c.ident=identification_address.ident;
or for this case
SELECT ident_c.name, ident_c.ident, identification_address.address, location.loc_name FROM identn_c LEFT JOIN location ON ident_c.ident=location.ident LEFT JOIN identification_address ON ident_c.ident=identification_address.ident;
Based on my little understanding of Django, JOIN instructions cannot be implemented. Hope I am wrong.
Django ORM take care of it if you set relationship between models.
for example,
models.py
class Aexample(models.Model):
name = models.CharField(max_length=20)
class Bexample(models.Model):
name = models.CharField(max_length=20)
fkexample = models.ForeignKey(Aexample)
shell
examplequery = Bexample.objects.filter(fkexample__name="hellothere")
SQL query
SELECT
"yourtable_bexample"."id",
"yourtable_bexample"."name",
"yourtable_bexample"."fkexample_id"
FROM "yourtable_bexample"
INNER JOIN "yourtable_aexample"
ON ("yourtable_bexample"."fkexample_id" = "yourtable_aexample"."id")
WHERE "yourtable_aexample"."name" = hellothere
you want to make query in Django like below
SELECT ident_c.name, ident_c.ident, identification_address.address, location.loc_name
FROM identn_c
LEFT JOIN location ON ident_c.ident=location.ident
LEFT JOIN identification_address ON ident_c.ident=identification_address.ident;
It means you want all rows from identn_c, right?. If you make proper relationship between your tables for your purpose, Django ORM takes care of it.
class IntentC(model.Model):
exampleA = models.ForeignKey(ExampleA)
exampleB = models.ForeignKey(ExampleB)
this command make query with JOIN Clause.
identn_instance = IdentC.objects.get(id=somenumber)
identn_instance.exampleA
identn_instance.exampleB
you can show every IntentC rows and relating rows in different tables.
for in in IntentC.objects.all(): #you can all rows in IntentC
print(in.exampleA.name)
#show name column in exampleA table
#JOIN ... ON intenctctable.example_id = exampleatable.id
print(in.exampleB.name) #show name column in exampleB table / JOIN ... ON

Advanced select with django ORM

I am using the following model:
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def __str__(self): # __unicode__ on Python 2
return "%s (%s)" % (self.name, ", ".join(topping.name
for topping in self.toppings.all()))
And now I want only the elements for vegetarian menu, filtered by tomatoes
pizza_item = Pizza.objects.filter(toppings__name='tomatoes')
My select is:
SELECT `pizza`.`id`, `pizza`.`name`
FROM `pizza`
INNER JOIN `pizza_toppings` ON (
`pizza`.`id` = `pizza_toppings`.`pizza_id` )
INNER JOIN `web_topping` ON (
`pizza_toppings`.`topping_id` = `topping`.`id` )
WHERE `topping`.`name` = azucar
but i want get:
SELECT `pizza`.`id`, `pizza`.`name`, `topping`.`name`
FROM `pizza`
INNER JOIN `pizza_toppings` ON (
`pizza`.`id` = `pizza_toppings`.`pizza_id` )
INNER JOIN `web_topping` ON (
`pizza_toppings`.`topping_id` = `topping`.`id` )
WHERE `topping`.`name` = azucar
This last query works fine in mysql db. And works using pizza.objects.raw but i want get using django ORM
Is a select with topping.name i try it using prefetch_select('toppings'). but i cant get the same select.
Have you tried using the values method for Queryset ?
Something like :
pizza_item = Pizza.objects.filter(toppings__name='tomatoes').values("id", "name", "toppings__name")
I am not sure if that's doable. Because when you use Pizza.objects... you are limited to the fields that are in the Pizza model. Since the Pizza model does not contain toppings' name field. You cannot retrieve it. You can only retrieve toppings' id field:
pizza_item = Pizza.objects.filter(toppings__name='tomatoes').values('id', 'name', 'toppings')
Which will provide "toppings"."topping_id" in SELECT.
Also, since you have specified that toppings__name='tomatoes, all of toppings' name will be tomatoes in this queryset, so what is the point of having topping.name in your result?

django query with ...objects.raw

How I can make a query with two models using this
model.objects.raw(...)
and into the sql query has the INNER JOIN with the another model(table) this is possible
model.objects.raw('
SELECT establecimiento.nombre, categoria.titulo
FROM establecimiento INNER JOIN
categoria ON establecimiento.categoria = categoria.id')
I need print the establecimiento's name with his categoria's name
class Establecimiento(models.Model):
nombre = models.CharField(max_length = 140)
categoria = models.ForeignKey(Categoria)
ciudad = models.ForeignKey(Ciudad)
def __unicode__(self):
return self.nombre
class Categoria(models.Model):
titulo = models.CharField(max_length = 140)
Fetching objects from the ORM automatically does any joins required and will return objects (instances of the models) which you can use to follow relationships.
If you simply fetch all your Establecimiento objects, you can access the related Categoria objects, like this:
all_objects = Establecimiento.objects.all()
for obj in all_objects:
print('Number: {} Category: {}'.format(obj.nombre, obj.categoria.titulo))
Or, if you want to fetch only those two specific properties, use values, like this:
all_objects = Establecimiento.objects.values('nombre','ciudad__titulo')
for obj in all_objects:
print('Number: {} Category: {}'.fromat(obj['nombre'],obj['ciudad__titulo']))