Django-queryset join without foreignkey
Asked Answered
T

5

33

model.py

class Tdzien(models.Model):
  dziens = models.SmallIntegerField(primary_key=True, db_column='DZIENS')
  dzienrok = models.SmallIntegerField(unique=True, db_column='ROK')


class Tnogahist(models.Model):
  id_noga = models.ForeignKey(Tenerg, primary_key=True, db_column='ID_ENERG')
  dziens = models.SmallIntegerField(db_column='DZIENS')

What I want is to get id_noga where dzienrok=1234. I know that dziens should be

dziens = models.ForeignKey(Tdzien)

but it isn't and I can't change that. Normally I would use something like

Tnogahist.objects.filter(dziens__dzienrok=1234)

but I don't know how to join and filter those tables without foreignkey.

Thimbleweed answered 25/10, 2013 at 12:54 Comment(0)
A
22

It's possible to join two tables by performing a raw sql query. But for this case it's quite nasty, so I recommend you to rewrite your models.py.

You can check how to do this here

It would be something like this:

from django.db import connection

def my_custom_sql(self):
    cursor = connection.cursor()    
    cursor.execute("select id_noga
                    from myapp_Tnogahist a
                    inner join myapp_Tdzien b on a.dziens=b.dziens
                    where b.dzienrok = 1234")
    row = cursor.fetchone()
    return row
Appetite answered 25/10, 2013 at 13:50 Comment(0)
F
24

No joins without a foreign key as far as I know, but you could use two queries:

Tnogahist.objects.filter(dziens__in=Tdzien.objects.filter(dzienrok=1234))

Florey answered 25/10, 2013 at 12:57 Comment(5)
this will do one query with a subquery.Coarsegrained
This is translated to an IN clause, which usually has a size limit. (~1000 items in most databases I'm familiar with)Lugo
@DanielDubovski that is not true #1010206Salop
@Salop you proved that postgres doesn't have a limit, that's just one case..#4722720 All I said is that this is something that people need to be aware of..Lugo
@DanielDubovski The above should not create an item based IN clause, but a sub-select, i.e. the id list from Tdzien.objects.filter(dzienrok=1234) does not get materialized first. It generates SQL as the answer in your linked post suggest, which does not fall under the IN item limitChannel
A
22

It's possible to join two tables by performing a raw sql query. But for this case it's quite nasty, so I recommend you to rewrite your models.py.

You can check how to do this here

It would be something like this:

from django.db import connection

def my_custom_sql(self):
    cursor = connection.cursor()    
    cursor.execute("select id_noga
                    from myapp_Tnogahist a
                    inner join myapp_Tdzien b on a.dziens=b.dziens
                    where b.dzienrok = 1234")
    row = cursor.fetchone()
    return row
Appetite answered 25/10, 2013 at 13:50 Comment(0)
S
9

To provide a little more context around @paul-tomblin's answer,

It's worth mentioning that for the vast majority of django users; the best course of action is to implement a conventional foreign key. Django strongly recommends avoiding the use of extra() saying "use this method as a last resort". However, extra() is still preferable to raw queries using Manager.raw() or executing custom SQL directly using django.db.connection

Here's an example of how you would achieve this using django's .extra() method:

Tnogahist.objects.extra(
    tables = ['myapp_tdzien'],
    where = [
        'myapp_tnogahist.dziens=myapp_tdzien.dziens',
        'myapp_tdzien.dzienrok=%s',
        ],
    params = [1234],
    )

The primary appeal for using extra() over other approaches is that it plays nicely with the rest of django's queryset stack, like filter, exclude, defer, values, and slicing. So you can probably plug it in alongside traditional django query logic. For example: Tnogahist.objects.filter(...).extra(...).values('id_noga')[:10]

Shifty answered 25/2, 2021 at 18:56 Comment(0)
D
7

Could you do this with .extra? From https://docs.djangoproject.com/en/dev/ref/models/querysets/#extra:

where / tables

You can define explicit SQL WHERE clauses — perhaps to perform non-explicit joins — by using where. You can manually add tables to the SQL FROM clause by using tables.

Decarbonate answered 2/4, 2014 at 18:22 Comment(0)
M
2

You could use following if you select some fields of two models without foreign key.

#import
from django.db.models import Subquery, OuterRef
from django.db.models.expressions import RawSQL

#result
dziens_result = Tdzien.objects.filter(dzienrok=1234).annotate(
                tno_dziens=Subquery(Tnogahist.objects.filter(dziens=OuterRef('dziens')).values('dziens')
                        )
                ).values_list('dziens', 'tno_dziens', 'dzienrok','id_noga')
Mundane answered 19/10, 2023 at 9:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.