Convert join SQL query into django - python

I am new to django and trying out stuff with it.
How do I display selected fields from the joined table.
For example:
I have two models, X and Y. I am merging these two models based on the foreign key of model Y.
class X(models.Model):
name = models.CharField()
id = models.AutoField(primary_key=True)
class Y(models.Model):
owner_user = models.ForeignKey(X, models.DO_NOTHING,
db_column='id')
detail = models.CharField()
How do I write this query as a django code?
SELECT name, id, Body_details
FROM X, Y
WHERE X.id = Y.OwnerUserId;

You can use select_related
a = Y.objects.select_related('OwnerUserId').all()
for object in a:
print(object.OwneruserId.name, object.OwneruserId.id, object.body)

You can make use of select_related here.
result = Y.objects.select_related('owner_use')
All the work behind joining will automatically be done by this ORM using select_related. You can see previously asked questions similar to this one here.

You need to use the related_name of the ForeignKey field, which is y_set by default, to access the reverse relationship of model :
some_id = 1
instance = X.objects.get(id=some_id)
instance.y_set.all()

Related

Get all the rows of a table along with matching rows of another table in django ORM using select_related

I have 2 models
Model 1
class Model1(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255)
type = models.CharField(max_length=255)
details = models.TextField(max_length=1000)
price = models.FloatField()
Model 2
class Model2(models.Model):
id = models.IntegerField(primary_key=True)
user_id = models.ForeignKey(
User,
on_delete=models.CASCADE
)
plan_selected = models.ForeignKey(Model1)
I am trying to check whether a user has selected any plans.
The field plan_selected is a foreign key for Model1 - id. I want to get all details of Model1 along with details of Model2 in a single line of the query set using Django.
So far I have tried to get is :
sub_details = Model1.objects.select_related('Model2').filter(user_id=id)
For select_related(), you want to select on the field name, not the related model's name. But all this does is that it adds a join, pulls all rows resulting from that join, and then your python representations have this relation cached (no more queries when accessed).
You also need to use __ to traverse relationships across lookups.
Docs here: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related
But you don't even need select_related() for your goal: "I am trying to check whether a user has selected any plans.". You don't need Model1 here. That would be, given a user's id user_id:
Model2.objects.filter(user_id=user_id).exists()
# OR if you have a User instance `user`:
user.model2_set.exists()
If however what you want is "all instances of Model1 related to user via a Model2":
Model1.objects.filter(model2__user_id=user_id).all()
to which you can chain prefetch_related('model2_set') (this is 1 -> Many so you're pre-fetching, not selecting - i.e fetches and caches ahead of time each results' related model2 instances in one go.)
However, that'd be easier to manage with a ManyToMany field with User on Model1, bypassing the need for Model2 entirely (which is essentially just a through table here): https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

Django: join two table on foreign key to third table?

I have three models
class A(Model):
...
class B(Model):
id = IntegerField()
a = ForeignKey(A)
class C(Model):
id = IntegerField()
a = ForeignKey(A)
I want get the pairs of (B.id, C.id), for which B.a==C.a. How do I make that join using the django orm?
Django allows you to reverse the lookup in much the same way that you can use do a forward lookup using __:
It works backwards, too. To refer to a “reverse” relationship, just use the lowercase name of the model.
This example retrieves all Blog objects which have at least one Entry whose headline contains 'Lennon':
Blog.objects.filter(entry__headline__contains='Lennon')
I think you can do something like this, with #Daniel Roseman's caveat about the type of result set that you will get back.
ids = B.objects.prefetch_related('a', 'a__c').values_list('id', 'a__c__id')
The prefetch related will help with performance in older versions of django if memory serves.

How to use unique_together method in django views

class Model1(models.Model):
username = models.CharField(max_length=100,null=False,blank=False,unique=True)
password = models.CharField(max_length=100,null=False,blank=False)
class Model2(models.Model):
name = models.ForeignKey(Model1, null=True)
unique_str = models.CharField(max_length=50,null=False,blank=False,unique=True)
city = models.CharField(max_length=100,null=False,blank=False)
class Meta:
unique_together = (('name', 'unique_str'),)
I've already filled 3 sample username-password in Model1 through django-admin page
In my views I'm getting this list as
userlist = Model1.objects.all()
#print userlist[0].username, userlist[0].password
for user in userlist:
#here I want to get or create model2 object by uniqueness defined in meta class.
#I mean unique_str can belong to multiple user so I'm making name and str together as a unique key but I dont know how to use it here with get_or_create method.
#right now (without using unique_together) I'm doing this (but I dont know if this by default include unique_together functionality )
a,b = Model2.objects.get_or_create(unique_str='f3h6y67')
a.name = user
a.city = "acity"
a.save()
What I think you're saying is that your logical key is a combination of name and unique_together, and that you what to use that as the basis for calls to get_or_create().
First, understand the unique_together creates a database constraint. There's no way to use it, and Django doesn't do anything special with this information.
Also, at this time Django cannot use composite natural primary keys, so your models by default will have an auto-incrementing integer primary key. But you can still use name and unique_str as a key.
Looking at your code, it seems you want to do this:
a, _ = Model2.objects.get_or_create(unique_str='f3h6y67',
name=user.username)
a.city = 'acity'
a.save()
On Django 1.7 you can use update_or_create():
a, _ = Model2.objects.update_or_create(unique_str='f3h6y67',
name=user.username,
defaults={'city': 'acity'})
In either case, the key point is that the keyword arguments to _or_create are used for looking up the object, and defaults is used to provide additional data in the case of a create or update. See the documentation.
In sum, to "use" the unique_together constraint you simply use the two fields together whenever you want to uniquely specify an instance.

How do you force only one relationship in django when multiple are possible?

I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.

Get foreign key objects in a single query

I have 2 models in my Django code:
class ModelA(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
class ModelB(models.Model):
category = models.CharField(max_length=255)
modela_link = models.ForeignKey(ModelA, 'modelb_link')
functions = models.CharField(max_length=255)
created_by = models.ForeignKey(User)
Say ModelA has 100 records, all of which may or may not have links to ModelB
Now say I want to get a list of every ModelA record along with the data from ModelB
I would do:
list_a = ModelA.objects.all()
Then to get the data for ModelB I would have to do
for i in list_a:
i.additional_data = i.modelb_link.all()
However, this runs a query on every instance of i. Thus making 101 queries to run.
Is there any way of running this all in just 1 query? Or at least less than the 101 queries.
I've tried putting in ModelA.objects.select_related().all() but this didn't seem to have any effect.
As Ofri says, select_related only works on forwards relations, not reverse ones.
There's no built-in way to automatically follow reverse relations in Django, but see my blog post for a technique to do it reasonably efficiently. The basic idea is to get all the related objects for every item at once, then associate them manually with their related item - so you can do it in 2 queries rather than n+1.
Django ORM is a good thing but some some things is better to do manually.
You may import connection cursor and execute raw sql in single query.
from django.db import connection
cur=connection.cursor()
cur.execute(query)
rows = cur.fetchall()
your query should look like (for MySQL)
SELECT * FROM appname_modela INNER JOIN appname_modelb ON appname_modela.id=appname_modelb.modela_link_id
The reason .select_related() didn't work, is that .select_related() is used to follow foreign keys. Your ModelA doesn't have a foreign key to ModelB. Its ModelB that has a foreign key to ModelA. (so a ModelA instance can have multiple ModelB instances related to it).
You could use this to do it in 2 queries, and a bit of python code:
list_b = ModelB.objects.all()
list_a = ModelA.objects.all()
for a in list_a:
a.additional_data = [b for b in list_b if b.modela_link_id==a.id]
from django.db.models import OuterRef, Subquery
newest = ModelB.objects.filter(modela_link=OuterRef('pk'))
ModelA.objects.annotate(newest=Subquery(newest))
https://docs.djangoproject.com/en/3.2/ref/models/expressions/#subquery-expressions

Categories