Django: Annotate table with field across a M2M mapping to another table - sql

I want to get competition.name from a list of submissions.
In my setup, competitions and teams share a M2M relationship (with an associated competition-team object. Each competition-team pair can submit any number of submissions. I now have a dashboard page which I am trying to create a table of all submissions by the team accompanied by the respective competition's name. The output should look like:
| Submission Name | Submission Date etc. | Competition Name |
| Sub01 | 2020-12-30 2000 | Competition01 |
I have trouble retrieving the competition name from the submissions. Here are my models:
class Competition(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30)
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 CompetitionSubmission(models.Model):
competitionteam_id = models.ForeignKey(CompetitionTeam, on_delete=models.CASCADE, db_column='competitionteam_id')
I wish to annotate a set of submissions with their respective competition names. I tried with:
submissions.annotate(competition_name=Subquery(Competition.objects.filter(id=Subquery(CompetitionTeam.objects.get(id=OuterRef('competitionteam_id')).competition_id)).values('name')))
"ValueError: This queryset contains a reference to an outer query and may only be used in a subquery."
I also tested with the following command:
CompetitionSubmission.objects.prefetch_related('competitionteam_id__competition_id')
It runs but the command seems to do nothing. I will update this post with other methods I try.
Thank you.
EDIT
submissions.annotate(competition_name=Subquery(Competition.objects.filter(id=Subquery(CompetitionTeam.objects.filter(id=OuterRef(OuterRef('competitionteam_id_id'))).values('competition_id'))).values('name')))
Seems to work correctly.

You can traverse ForeignKeys directly using double underscores.
CompetitionSubmission.objects.values(
'competitionteam_id',
'competitionteam_id__competition_id',
'competitionteam_id__competition_id__name',
'competitionteam_id__team_id',
'competitionteam_id__team_id__name',
)
This will only produce a single database query. Django ORM takes care of everything.
P.S. I would avoid using '_id' in field names as Django model fields are supposed to be referring to related objects themselves. Django automatically adds extra attributes with '_id' that contains the related object's id. Please see https://docs.djangoproject.com/en/3.1/ref/models/fields/#database-representation

Related

Django Filter results of Table with matching id from another table

I have 3 tables
ManagedOrgDocument
Entitlements
DocumentEntitlementMapping
User can upload document and entitlement using different forms, however some entitlements have link with documents and documents have link with entitlement. The 3rd table maps document and entitlement
class EntitlementDocumentMapping(models.Model):
uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
managed_org_uuid = models.CharField(max_length=400)
document = models.ForeignKey(ManagedOrgDocument, on_delete=models.CASCADE)
entitlement = models.ForeignKey(ManagedOrgOraLicenseEntitlement, on_delete=models.CASCADE)
managed_org_documents =ManagedOrgDocument.objects.filter(managed_org_uuid=managed_org_uuid).order_by(*sort_order)
When I fetch all documents I want records from
EntitlementDocumentMapping table as well where Documents.id matches
with EntitlementDocumentMapping.document.uuid
You can use F() for this:
from django.db.models import F
managed_org_documents =ManagedOrgDocument.objects.filter(managed_org_uuid=F('document__uuid')).order_by(*sort_order)
F() allows you to access value from same row or related table's row.
Update
You do not need to additional operations to get documents. You can simply get them by:
# example 1
for docs in managed_org_documents:
print(docs.document)
# example 2
managed_org_documents.values('document')
You can use select_related to pre fetch document information so that calling docs.document does not make additional DB hits.
managed_org_documents.select_related('document')

How to Annotate Specific Related Field Value

I am attempting to optimize some code. I have model with many related models, and I want to annotate and filter by the value of a field of a specific type of these related models, as they are designed to be generic. I can find all instances of the type of related model I want, or all of the models related to the parent, but not the related model of the specific type related to the parent. Can anyone advise?
I initially tried
parents = parent.objects.all()
parents.annotate(field_value=Subquery(related_model.objects.get(
field__type='specific',
parent_id=OuterRef('id'),
).value)))
But get the error This queryset contains a reference to an outer query and may only be used in a subquery. When I tried
parents = parent.objects.all()
parents.annotate(field_value=Q(related_model.objects.get(
field__type='specific',
parent_id=F('id'),
).value)))
I get DoesNotExist: related_field matching query does not exist. which seems closer but still does not work.
Model structure:
class parent(models.Model):
id = models.IntegerField(null=False, primary_key=True)
class field(models.Model):
id = models.IntegerField(null=False, primary_key=True)
type = models.CharField(max_length=60)
class related_model(models.Model):
parent = models.ForeignKey(parent, on_delete=models.CASCADE, related_name='related_models')
field = models.ForeignKey(field, on_delete=models.CASCADE, related_name='fields')
Is what I want to do even possible?
Never mind I decided to do a reverse lookup, kinda like
parent_ids = related_model.objects.filter(field__type='specific', parent_id__in=list_of_parents).values_list('parent_id')
parents.objects.filter(id__in=parents_id)

Django one-to-many queries

Django newbie here.
I created three models: Band, Album and AlbumType (Live, EP, LP). Album have two foreign keys (from band and albumtype). Ok, so, in the view a make something like this:
bandList = Band.objects.all()
To retrieve all the bands but I can't figure out how to get all the Albums from a Band in the same view.
Any help will be appreciated. Thanks.
By default the related objects (through a ForeignKey on the other model) are accessible trough <modelname>_set zo in this case that is band.album_set (note this is a Manager attribute so you will probably use band.album_set.all() most of the time).
I personally prefer to give al my ForeignKey fields a related_name so I can name the attribute on the other model. The next example gives the Band the attribute band.albums.
class Band(models.Model):
# Band definition
class Album(models.Model):
band = models.ForeignKey(Band, related_name='albums')
# Rest of album definition
Could be great if you share your models definition. But I hope this helps you:
If you want to retrieve the Albums for a specific band:
band = Band.objects.get(...)
band_albums = Album.objects.filter(band=band)
That will return the albums for a band.
If you want retrive albums for all the bands:
bandList = Band.objects.all().select_related('album_set')
This will return the bans as before, but will cache the albums.

confusion over which relationship to use in models for these two tables

I have a user table that consists of these columns:
| id | username | password | email | pants_size_id | shirt_size_id |
pants_size_id and shirt_size_id are filled with foreign tables id keys where I store a list of sizes for pants and shirts in different country specific measures, example of pants_size table:
| id | UK_sizing | US_sizing | IT_sizing |
a single user will have only one pants and shirt size so the user table is filled with the ID of the corresponding rows in the size tables.
what kind of relationship does this imply between the user model and the pants and shirt sizing models?
Also how can I retrieve the data inside the foreign table column (example IT_sizing) when returning auth user return \Auth::user(); instead of the numeric size_id ?
In other words how can I retrieve say '32' (a pants size) instead of the pants_size_id (let's say '1').
Cato has the right answer, I can't exactly respond to it because of my rep but the logic in your other answer doesn't make sense from a relational standpoint.
Users don't belong to a size, instead, Users have a size.
To me it sounds like you mixed up the foreign and local key assignment it should be User->hasOne(pants_size).
In your model it would be the following, the explicitness of the keys isn't great, but if you have some weird thing laravel can't figure out this should work.
public function pants_size(){
return $this->hasOne('App\Pants_size','id','pants_size_id');
}
public function shirt_size(){
return $this->hasOne('App\Shirt_size','id','shirt_size_id');
}
To answer the other question of how to find the size (32), since you're dealing with three different measurements you have to have a where clause on the specific measurement the 32 represents, and get the id. If you specifically wanted the users you would call the eloquent query as so:
\Auth::User()->pants_size()->(..whatever measurement you want..)
Create a function establishing a hasOne relationship for both pants_size and shirt_size in the user model. Be sure to set the foreign key and local key correctly if you don't want Laravel to assume default keys (see here for details).
Once the functions are created, you will be able to obtain data about the user's size information like so: App\Model\User::find(123)->pants_size->UK_sizing. (This example is for a user with ID of 123).
this is how I made it work:
in USER model:
public function pants_size(){
return $this->belongsTo('App\Pants_size');
}
public function shirt_size(){
return $this->belongsTo('App\Shirt_size');
}
In Pants_size and Shirt_size Models:
public function user(){
return $this->hasMany('App\User');
}
That last one works also with hasOne.
The code I use to retrieve the data is:
public function index()
{
echo $user = User::find($id);
echo $pants = User::find($id)->pants_size->it_sizing;
echo $shirt = User::find($id)->shirt_size->it_sizing;
}

Ruby on Rails: How to implement 2 sortable columns ( pupil, mentor) for the same column in DB (user_profile)?

In order to implement "sortable columns" in Rails, i used as example the framework build by Ryan Bates in Railscast 228.
I have an application where one "mentor" teaches one "pupil" during one "class". The identification data (first name, last name, date of birth) for both persons ( mentor, pupil) are saved in a single DB column "user_profiles".
To visualize the schedule of all "classes", the application renders a table:
Pupil | Teacher | Class name | START | END.
While the "joins" logic in the controller (index method) is clear & simple, in the view ( V ), you need to specify the name of the DB column for "sortable". Here I encounter an ambiguous situation:
%th
= sortable ('Pupil', 'user_profiles.last_name')
%th
= sortable ('Mentor', 'user_profiles.last_name')
%th
Class name
%th
...
Do you have any idea how to instruct Rails to differentiate between the two ?
(When click on "Pupil", sort those elements from "user_profiles.last_name" belonging to "Pupil". Idem for "mentor")
Thanks,
Ciprian
First of all, I suggest you to use jqGrid jquery plugin for sorting. Its easy to integrate and use. It is also preferable as per UI design point of view.
Another thing, you should use different columns OR you can use alias OR you can apply sorting on set of columns.