Django ORM INNER JOIN - python

I have not been able to do this query with Django ORM.
how to make a inner join, how to do this query and return only the columns I want?
SELECT establecimiento.nombre, categoria.titulo
FROM establecimiento INNER JOIN
categoria ON establecimiento.categoria = categoria.id

Based on the information in your comment responding to pdxwebdev (that you have a foreign key field declared) this is simple. Django automates much of the join behavior needed for foreign key relationships.
To precisely replicate that query, including selecting only two fields from the join, any of values, values_list or only should do it depending on exactly what Python objects you want to get back. Eg, here's a query using values to retrieve an iterable queryset of dictionaries:
Establecimiento.objects.values('nombre', 'categoria__titulo')
values_list will retrieve tuples instead of dictionaries, and only will retrieve Establecimiento instances on which all model fields other than those two are deferred (they have not been retrieved from the database but will be looked up as needed).
When you use __ to follow a foreign key relationship like that, Django will do the inner join automatically.
You can also use select_related on a queryset to ask it to do the join even when you're not retrieving specific fields. EG:
Establecimiento.objects.select_related('categoria')
This should produce a query of SELECT * from ..., and return a queryset of Establecimiento instances that have their categoria data already loaded into memory.

I'm not sure I understand the question.
establecimiento.categoria just needs to be a foreign key field to categoria model. categoria.id is the primary key so this will be done automatically.
To return only certain columns, just the .only() method.
https://docs.djangoproject.com/en/dev/ref/models/querysets/#only

Related

Django ORM Group By with Foreign Keys

user is a foreign key on tournament.
select u.id, u.display_name, count(t.id)
from tournament t join user u
on t.user_id = u.id
where date(t.start_date)> '2022-07-01'
group by u.display_name, u.id
How can I make the above SQL query work with django's ORM?
In the majority of cases trying to translate an sql query into Django ORM syntax isn't the way to go.
From what i understand, you want to count tournaments, filtered with a date, bound to an user.
Try something like:
UserModel.objects.annotate(tournament_count=Count("tournament", filter=Q(start_date__gt=my_date)))
The annotate method allows for additionnal columns to be present in the ResultSet moslty related or calculated ones. ("tournament" is name of your Tournament model, if you defined a related_name for the user FK, use this name instead)
If you really want a group by, take a look at this How to query as GROUP BY in django?

How to implement a specific SQL statement as a SQLAlchemy ORM query

I was following a tutorial to make my first Flask API (https://medium.com/#dushan14/create-a-web-application-with-python-flask-postgresql-and-deploy-on-heroku-243d548335cc) I did it but now I want to do queries more custom with SQLAlchemy and PostgreSQL. My question is how I could do something like this:
query = text("""SELECT enc.*, persona."Persona_Nombre", persona."Persona_Apellido", metodo."MetEnt_Nombre", metodo_e."MetPag_Descripcion"
FROM "Ventas"."Enc_Ventas" AS enc
INNER JOIN "General"."Persona" AS persona ON enc."PersonaId" = persona."PersonaId"
INNER JOIN "Ventas"."Metodo_Entrega" AS metodo ON enc."MetodoEntregaId" = metodo."MetodoEntregaId"
INNER JOIN "General"."Metodo_Pago" AS metodo_e ON enc."MetodoPagoId" = metodo_e."MetodoPagoId"
INNER JOIN "General"."Estatus" AS estado ON enc. """)
but with SQLAlchemy in order to use the models that I created previously. Thanks in advance for any answer!!
Edit:
The columns that I wish to see at the final result are: enc.*, persona."Persona_Nombre", persona."Persona_Apellido", metodo."MetEnt_Nombre", metodo_e."MetPag_Descripcion"
I really wish I could share more info but sadly I can't at the moment.
Doing this from the ORM layer, you would reference model names (I match the names of your query above, but I'm sure some of the model/table names are off - now adjusted slightly).
Now revised to include the specific columns you only want to see (note that I ignore your SQL aliases, ORM layer handles the actual query construction):
selection = session.query(Enc_Ventas, Persona.Persona_Nombre, Persona.Persona_Apellido, Metodo_Entrega.MetEnt_Nombre, Metodo_Pago.MetPag_Descripcion).\
join(Persona, Enc_Ventas.PersonaId == Persona.PersonaId).
join(Metodo_Entrega, Enc_Ventas.MetodoEntregaId == Metodo_Entrega.MetodoEntregaId).\
join(Metodo_Pago, Enc_Ventas.MetodoPagoId == Metodo_Pago.MetodoPagoId).\
join(Estatus).all()
Referencing the selection collection would be by iteration through the rows of tuples. A more robust and stable solution would be to transform each output row into a dict.
Otherwise, by including whole models, the collection of rows returned can be individually accessed by referencing as dot notation the model names in the query().
If you need further access to the columns in the related tables, use the ORM technique of .options(joinedload(myTable)), which in a single database query will bring in those additional columns, using the relationship name, also as dot notation.
You also need to define sqlalchemy relationships within your models for this to work, as well as defining the underlying SQL foreign keys.
Much more detail and/or a more specific question is needed to help further, imo.

Ordering queryset by filtered child objects

I have the following (simplified) data model, see visual representation below the post:
Articles, which have Attributes
Attributes refer by PK to a Type, which have a field code
Attributes have a field value
The value refers to the field uuid in another model called Record, which also has a field sorting_code
I now want to return a list of articles in a certain ordering, using pagination. I am using a default ViewSet for it. The pagination forces me to do the ordering in database, instead of later in Python. However, I cannot seem to find the correct ordering clause that orders these articles. What I need to do is:
Fetch the Attribute with a specific type
Look up the values in those Attributes in the Record table
Order the articles based by sorting_code
The following SQL query does the job (for all articles):
SELECT art.order_id, art.uuid, att.value, mdr.code, mdr.name, mdr.sorting_code
FROM ow_order_article art
INNER JOIN ow_order_attribute att ON att.article_id = art.uuid
INNER JOIN ow_masterdata_type mdt ON att.masterdata_type_id = mdt.uuid
INNER JOIN ow_masterdata_record mdr ON att.value = mdr.uuid
WHERE mdt.code = 'article_structure'
ORDER BY mdr.sorting_code, mdr.code
What would be the correct way to get this ordering in a queryset in Django?

Django model search concatenated string

I am trying to use a Django model to for a record but then return a concatenated field of two different tables joined by a foreign key.
I can do it in SQL like this:
SELECT
location.location_geoname_id as id,
CONCAT_WS(', ', location.location_name, region.region_name, country.country_name) AS 'text'
FROM
geonames_location as location
JOIN
geonames_region as region
ON
location.region_geoname_id = region.region_geoname_id
JOIN
geonames_country as country
ON
region.country_geoname_id = country.country_geoname_id
WHERE
location.location_name like 'location'
ORDER BY
location.location_name, region.region_name, country.country_name
LIMIT 10;
Is there a cleaner way to do this using Django models? Or do I need to just use SQL for this one?
Thank you
Do you really need the SQL to return the concatenated field? Why not query the models in the usual way (with select_related()) and then concatenate in Python? Or if you're worried about querying more columns than you need, use values_list:
locations = Location.objects.values_list(
'location_name', 'region__region_name', 'country__country_name')
location_texts = [','.join(l) for l in locations]
You can also write raw query for this in your code like that and later on you can concatenate.
Example:
org = Organization.objects.raw('SELECT organization_id, name FROM organization where is_active=1 ORDER BY name')
Keep one thing in a raw query you have to always fetch primary key of table, it's mandatory. Here organization_id is a primary key of contact_organization table.
And it's depend on you which one is useful and simple(raw query or model query).

Django: Selecting distinct values on maximum foreign key value

I have the following models which I'm testing with SQLite3 and MySQL:
# (various model fields extraneous to discussion removed...)
class Run(models.Model):
runNumber = models.IntegerField()
class Snapshot(models.Model):
t = models.DateTimeField()
class SnapshotRun(models.Model):
snapshot = models.ForeignKey(Snapshot)
run = models.ForeignKey(Run)
# other fields which make it possible to have multiple distinct Run objects per Snapshot
I want a query which will give me a set of runNumbers & snapshot IDs for which the Snapshot.id is below some specified value. Naively I would expect this to work:
print SnapshotRun.objects.filter(snapshot__id__lte=ss_id)\
.order_by("run__runNumber", "-snapshot__id")\
.distinct("run__runNumber", "snapshot__id")\
.values("run__runNumber", "snapshot__id")
But this blows up with
NotImplementedError: DISTINCT ON fields is not supported by this database backend
for both database backends. Postgres is unfortunately not an option.
Time to fall back to raw SQL?
Update:
Since Django's ORM won't help me out of this one (thanks #jknupp) I did manage to get the following raw SQL to work:
cursor.execute("""
SELECT r.runNumber, ssr1.snapshot_id
FROM livedata_run AS r
JOIN livedata_snapshotrun AS ssr1
ON ssr1.id =
(
SELECT id
FROM livedata_snapshotrun AS ssr2
WHERE ssr2.run_id = r.id
AND ssr2.snapshot_id <= %s
ORDER BY snapshot_id DESC
LIMIT 1
);
""", max_ss_id)
Here livedata is the Django app these tables live in.
The note in the Django documentation is pretty clear:
Note:
Any fields used in an order_by() call are included in the SQL SELECT columns. This can sometimes lead to unexpected results when used in conjunction with distinct(). If order by fields from a related model, those fields will be added to the selected columns and they may make otherwise duplicate rows appear to be distinct. Since the extra columns don’t appear in the returned results (they are only there to support ordering), it sometimes looks like non-distinct results are being returned.
Similarly, if you use a values() query to restrict the columns selected, the columns used in any order_by() (or default model ordering) will still be involved and may affect uniqueness of the results.
The moral here is that if you are using distinct() be careful about ordering by related models. Similarly, when using distinct() and values() together, be careful when ordering by fields not in the values() call.
Also, below that:
This ability to specify field names (with distinct) is only available in PostgreSQL.

Categories