For the below sample schema
# schema sameple
class A(models.Model):
n = models.ForeignKey(N, on_delete=models.CASCADE)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class N(models.Model):
id = models.AutoField(primary_key=True, editable=False)
d = models.ForeignKey(D, on_delete=models.PROTECT)
class D(models.Model):
dsid = models.CharField(max_length=255, primary_key=True)
class P(models.Model):
id = models.AutoField(primary_key=True, editable=False)
name = models.CharField(max_length=255)
n = models.ForeignKey(N, on_delete=models.CASCADE)
# raw query for the result I want
# SELECT P.name
# FROM P, N, A
# WHERE (P.n_id = N.id
# AND A.n_id = N.id
# AND A.d_id = \'MY_DSID\'
# AND P.name = \'MY_NAME\')
What am I trying to achieve?
Well, I’m trying to find a way somehow be able to write a single queryset which does the same as what the above raw query does. So far I was able to do it by writing two queryset, and use the result from one queryset and then using that queryset I wrote the second one, to get the final DB records. However that’s 2 hits to the DB, and I want to optimize it by just doing everything in one DB hit.
What will be the queryset for this kinda raw query ? or is there a better way to do it ?
Above code is here https://dpaste.org/DZg2
You can archive it using related_name attribute and functions like select_related and prefetch_related.
Assuming the related name for each model will be the model's name and _items, but it is better to have proper model names and then provided meaningful related names. Related name is how you access the model in backward.
This way, you can use this query to get all models in a single DB hit:
A.objects.all().select_related("n", "d", "n__d").prefetch_related("n__p_items")
I edited the code in the pasted site, however, it will expire soon.
Related
Good day.
I wish to annotate my model with information from a different table.
class CompetitionTeam(models.Model):
competition_id = models.ForeignKey('Competition', on_delete=models.CASCADE, to_field='id', db_column='competition_id')
team_id = models.ForeignKey('Team', on_delete=models.CASCADE, to_field='id', null=True, db_column='team_id')
...
class Team(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
teamleader_id = models.ForeignKey('User', on_delete=models.CASCADE, to_field='id', db_column='teamleader_id')
...
class Competition(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
...
Looping through my competitions, I wish to retrieve the list of competitionteam objects to be displayed with the relevant team's name. I tried:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=...)
-where instead of the ellipses I put Subquery expressions in. However, I'm unsure of how to match the team_id variable. eg.
*.anotate(name=Subquery(Team.objects.filter(id=competitionteam.team_id)).values('name'))
Related is the question: Django annotate field value from another model but I am unsure of how to implement that in this case. In that case, in place of mymodel_id, I used team_id but it only had parameters from the Team object, not my competition team object. I didn't really understand OuterRef but here is my attempt that failed:
CompetitionTeam.objects.filter(competition_id=_competition.id).filter(team_id__in=joined_team_ids).annotate(name=Subquery(Team.objects.get(id=OuterRef('team_id'))))
"Error: This queryset contains a reference to an outer query and may only be used in a subquery."
The solution for my question was:
CompetitionTeam.objects.filter(
competition_id=_competition.id,
team_id__in=joined_team_ids
).annotate(
name=Subquery(
Team.objects.filter(
id=OuterRef('team_id')
).values('name')
))
Thanks.
class Docs(models.Model):
doc_id = models.BigIntegerField(primary_key=True)
journal = models.CharField(max_length=50, blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'docs'
class Assays(models.Model):
assay_id = models.BigIntegerField(primary_key=True)
doc = models.ForeignKey('Docs', models.DO_NOTHING)
description = models.CharField(max_length=4000, blank=True, null=True)
class Meta:
managed = False
db_table = 'assays'
class Activities(models.Model):
activity_id = models.BigIntegerField(primary_key=True)
assay = models.ForeignKey(Assays, models.DO_NOTHING)
doc = models.ForeignKey(Docs, models.DO_NOTHING, blank=True, null=True)
record = models.ForeignKey('CompoundRecords', models.DO_NOTHING)
class Meta:
managed = False
db_table = 'activities'
I apologize in advance if this answer is easily found elsewhere. I have searched all over and do not see a simple way to query my data as intuitively as I feel like should be possibe.
These are classes for 3 tables. The actual dataset is closer to 100 tables. Each doc_id can have one or many associated activity_ids. Each activity_id is associated with one assay_id.
My goal is to obtain all of the related data for each of the activities in a single doc. For instance:
query_activities_values = Docs.objects.get(doc_id=5535).activities_set.values()
for y in query_activities_values:
print(y)
break
>>> {'activity_id': 753688, 'assay_id': 158542, 'doc_id': 5535, .....
This returns 32 dictionaries (only part of the first is shown) for columns in the Activities table that have doc_id=5535. I would like to go one step further and also automatically pull in all of the data from the Assays table that is associated with the corresponding assay_id for each dictionary.
I can access that Assay data through a similar query, but only by stating each field explicitly:
query_activities_values = Docs.objects.get(doc_id=5535).activities_set.values('assay', 'assay__assay_type', 'assay__description')
for y in query_activities_values:
print(y)
break
I would like a single query that finds not only the assay and associated assay data for one activity_id, but finds all data and associated data for the 90+ other tables associated in the model
Thank you
Update 1
I did find this code that works surprisingly well for my needs, however, I was curious if this is the best method:
from django.forms.models import model_to_dict
def serial_model(modelobj):
opts = modelobj._meta.fields
modeldict = model_to_dict(modelobj)
for m in opts:
if m.is_relation:
foreignkey = getattr(modelobj, m.name)
if foreignkey:
try:
modeldict[m.name] = serial_model(foreignkey)
except:
pass
return modeldict
That's not too much code, but I thought there may be a more built-in way to do this.
What you need is prefetch_related:
Django 2.2 Prefetch Related Docs
query_activities_values = Docs.objects.get(doc_id=5535).activities_set.values()
Would become:
query_activities_values = Docs.objects.prefetch_related(models.Prefetch("activities_set", to_attr="activities"), models.Prefetch("assays_set", to_attr="assays")).get(doc_id=5535)
A new attributes will be created called "activities" and "assays" which you can use to retrieve data.
One more thing. This isn't actually 1 query. It's 3. However, if you're getting more than just one object from Docs, it's still going to be 3.
Also, is there a reason why you're using BigIntegerField?
Forgive me if the question does not make sense, trying to teach myself django. I've been trying to search how to do this but i'm not sure if i'm using the right words in my search.
I have the following models.
class Category(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupHeader(models.Model):
code = models.CharField(max_length=10, unique=True)
description = models.CharField(max_length=50)
class UserGroupDetail(models.Model):
usergroupheader = models.ForeignKey(UserGroupHeader, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
How do i get a query set from the Category model using the UserGroupHeader? so far what i've got is something like this UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all(), now from the result of this how do i get the Category model?
I'm not sure if I understood exactly what you are trying to do, but in general, while querying, you can follow relations using double underscores. Below are a couple of possible queries:
my_group_header = UserGroupHeader.objects.get(...)
Category.objects.filter(usergroupdetail__usergroupheader=my_group_header) # Gets Category objects related to my_group_header through UserGroupDetail model
Category.objects.filter(usergroupdetail__usergroupheader__code='abc') # Gets Category objects related to UserGroupHeader object with code 'abc' through UserGroupDetail model
UserGroupHeader.objects.filter(usergroupdetail__category__code='abc') # Gets UserGroupHeader objects related to Category object with code 'abc' through UserGroupDetail model
Your query UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all() would return a QuerySet of UserGroupDetail objects. In order to get the category of each UserGroupDetail, you can:
for user_group_detail in UserGroupHeader.objects.get(pk=9).usergroupdetail_set.all():
category = user_group_detail.category
print(category. description)
Or something similar according to your needs
I'm learning Django and trying to get the hang of querying foreign keys across a bridging table. Apologies if this is a duplicate, I haven't been able to find the answer by searching. I've got models defined as follows
class Place(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
class PlaceRef(models.Model):
place = models.ForeignKey(Place) # many-to-one field
entry = models.ForeignKey(Entry) # many-to-one field
class Entry(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=10)
If I want to retrieve all the Entries associated with a particular Place, how do I do it?
place = get_object_or_404(Place, id=id)
placerefs = PlaceRef.objects.filter(place=place)
entries = Entry.objects.filter(id.....)
Also, if there is a more sensible way for me to define (or get rid of) PlaceRefs in Django, please feel free to suggest alternatives.
Thanks for helping out a beginner!
First, I'd suggest rewriting the models to:
class Place(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
class Entry(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=10)
places = models.ManyToManyField(Place, related_name='places')
So you can query:
Entry.objects.filter(places__id=id)
In your current model:
Entry.objects.filter(placeref_set__place__id=id)
Note that the double underscore __ is used to jump from one model to the next. Also, django creates some fields on the model that help you navigate to related objects. In this example: Entry.placeref_set. You can read more about it here:
http://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward
I've got Django tables like the following (I've removed non-essential fields):
class Person(models.Model):
nameidx = models.IntegerField(primary_key=True)
name = models.CharField(max_length=300, verbose_name="Name")
class Owner(models.Model):
id = models.IntegerField(primary_key=True)
nameidx = models.IntegerField(null=True, blank=True) # is Person.nameidx
structidx = models.IntegerField() # is PlaceRef.structidx
class PlaceRef(models.Model):
id = models.IntegerField(primary_key=True)
structidx = models.IntegerField() # used for many things and not equivalent to placeidx
placeidx = models.IntegerField(null=True, blank=True) # is Place.placeidx
class Place(models.Model):
placeidx = models.IntegerField(primary_key=True)
county = models.CharField(max_length=36, null=True, blank=True)
name = models.CharField(max_length=300)
My question is as follows. If in my views.py file, I have a Person referenced by name and I want to find out all the Places they own as a QuerySet, what should I do?
I can get this far:
person = Person.objects.get(name=name)
owned_relations = Owner.objects.filter(nameidx=nameidx)
How do I get from here to Place? Should I use database methods?
I'm also not sure if I should be using ForeignKey for e.g. Owner.nameidx.
Thanks and apologies for this extremely basic question. I'm not sure how to learn the basics of database queries except by trying, failing, asking SO, trying again... :)
The whole point of foreign keys is for uses like yours. If you already know that Owner.nameidx refers to a Person, why not make it a foreign key (or a OneToOne field) to the Person table? Not only do you get the advantage of referential integrity - it makes it impossible to enter a value for nameidx that isn't a valid Person - the Django ORM will give you the ability to 'follow' the relationships easily:
owned_places = Place.objects.filter(placeref__owner__person=my_person)
will give you all the places owned by my_person.
Incidentally, you don't need to define the separate primary key fields - Django will do it for you, and make them autoincrement fields, which is almost always what you want.
If u could redesign.Then
In owner nameidx can be a foreign key to Person(nameidx)
Placeref(structidx) could be a foreign key to Owner(structidx) and
Place(placeidx) could be a foreign key Place ref(placeidx)
Then u could deduce the place value easily..