Django 2 multi-table joins? - django

Python 3.6 / Django 2
I have a Profile model that defines and "extended" User model. I have a Group model that defines some internal, app related groups; and, a ProfileGroupLink model that defines the possible group(s) a profile might belong to.
Let's say the tables look like:
Profile
----------
id int
name_last varchar(32)
name_first varchar(32)
account_number uuid
Group
----------
id uuid
name varchar(32)
ProfileGroupLink
----------
id int
groupLnk uuid foreign key(Group.id)int
profileLnk uuid foreign key(Profile.account_number)
I have the active user "profile" from:
my_profile = Profile.objects.get(user=request.user)
I would normally write an SQL query something like:
select Group.name
from Group as Group
inner join Profile as Profile
on ? = Profile.id
inner join ProfileGroupLink as ProfileGroupLink
on Profile.id = ProfileGroupLink.profileLnk
and Group.id = ProfileGroupLink.groupLnk
order by Group.name
and pass the profile id in as a parameter. I am looking at the page: https://docs.djangoproject.com/en/2.0/topics/db/queries/ but I am not getting the double underscore syntax for joins.
My current attempt is:
groups = Group.objects.all() \
.filter(GroupList__profile=my_profile.account_number) \
.filter(GroupList__group=Group__group_id)
but PyCharm flags "Group__group_id" as an unresolved reference. I am importing the models. How do I do this in Django?

I'm going to assume that what you want to get the query is to obtain the groups that belong to a profile
groups = Group.objects.filte(profilegrouplink__profilelnk=my_profile)
note that I put everything in lowercase, do not need to put all the joins and do not use the models to make the filters, the attributes are used

Related

Which one is better for creating a follower-following system, Foreign Key or ManyToMany in django?

Which one would be optimal for creating the followers-following system in Django.
1.Foreign Key
class Follower(models.Model):
current_user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='following')
following_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='followers')
2.Many-To-Many
class Follower(models.Model):
current_user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='following')
followers = models.ManyToManyField(User)
So, for instance, if we want to retrive the following list of the user:
1.Foreign Key
Follower.objects.get(following_id=1).following.all()
2 Many-To-Many
User.object.get(user=1).follower_set.all().values('current_user')
If you want to add metadata about someone following you don't have much choice but to use a M2M with a through table. Otherwise, you might as well use option 1.

Inner join on Django with where clause?

I'm trying to filter out a ModelForm to only display dropdown values tied to a specific user.
I've got three tabled tied together:
User, Project, ProjectUser.
One user can have many projects, and one projects can have many users, and the table ProjectUser is just a joined table between User and Project, e.g
id | project_id | user_id
1 5 1
2 6 2
3 6 1
How would I write the following inner join query in Django ORM?
SELECT name
FROM projectuser
INNER JOIN project
ON projectuser.project_id = project.id
WHERE user_id = <request.user here>
When Django ORM applies a Filter to a field specified as a foreign key, Django ORM understands that it is a related table and joins it.
Project.objects.filter(projectuser__user=user)
You can join multiple tables, or even apply a filter to the reverse of a foreign key! You can use the related_name of the ForeignKey field appropriately.
You original SQL
SELECT name
FROM projectuser
INNER JOIN project
ON projectuser.project_id = project.id
WHERE user_id = <request.user here>
So as i see your SQL, you want to get list of name from projectuser for specific user. If so, here is the answer
ProjectUser.objects.filter(user_id = user).values_list('name', flat = True)
I see you accept answer with Project.objects.filter(projectuser__user=user)
For this answer your SQL should look like this
SELECT name
FROM project
INNER JOIN projectuser
ON projectuser.project_id = project.id
WHERE projectuser.user_id = <request.user here>

Django filter exclude with ReverseManager

I have a model Tenant and a model SocialMediaContextTenant that has a foreign key to the Tenant model.
class SocialMediaContextTenant(AbstractSocialMediaContext):
tenant = models.ForeignKey('campaigns.Tenant')
secret = models.CharField(...)
I would like filter all tenants where a connected SocialMediaContextTenant has the secret field set.
So far I have this query
Tenant.objects
.exclude(socialmediacontexttenant__secret='')
.values_list('id', flat=True)
But the exclude filter does not seem to work, since I still get results with an empty secret field
Looks like secret field has null value, not empty string. To exclude these records use __isnull lookup:
Tenant.objects.exclude(socialmediacontexttenant__secret='').exclude(socialmediacontexttenant__secret__isnull=True)

Converting a Query to Django ORM Code

I want to perform this query to get the id of all the groups that are associated with the table auth_group_permissions.
select id from auth_group where id in (select group_id from auth_group_permissions group by group_id)
Models Group and Permission are connected to each other via ManyToMany field permissions. See the source
Based on the info I found in internet and based on what I remember, you need to do the next:
from django.contrib.auth.models import Group, Permission
grouped_permissions = Permission.objects.order_by().distinct('group_id')
your_ids = Group.objects.filter(permissions__group_id__in=grouped_permissions).values_list('id', flat=True)
That's the final correct answer.
grouped_permissions = Permission.objects.filter().order_by().distinct().values_list('group__id',flat=True)
your_ids = Group.objects.filter(id__in = grouped_permissions)

Finding multiple instances of a tag with Django "through" field

I run a lab annotation website where users can annotate samples with tags relating to disease, tissue type, etc. Here is a simple example from models.py:
from django.contrib.auth.models import User
from django.db import models
class Sample(models.Model):
name = models.CharField(max_length = 255)
tags=models.ManyToManyField('Tag', through = 'Annot')
class Tag(models.Model):
name = models.CharField(max_length = 255)
class Annot(models.Model):
tag = models.ForeignKey('Tag')
sample = models.ForeignKey('Sample')
user = models.ForeignKey(User, null = True)
date = models.DateField(auto_now_add = True)
I'm looking for a query in django's ORM which will return the tags in which two users agree on the annotation of same tag. It would be helpful if I could supply a list of users to limit my query (if someone only believes User1 and User2 and wants to find the sample/tag pairs that only they agree on.)
I think I understood what you need. This one made me think, thanks! :-)
I believe the equivalent SQL query would be something like:
select t.name, s.name, count(user_id) count_of_users
from yourapp_annot a, yourapp_tag t, yourapp_sample s
where a.tag_id = t.id
and s.id = a.sample_id
group by t.name, s.name
having count_of_users > 1
While I try hard not to think in SQL when I'm coming up with django model navigation (it tends to get in the way); when it comes to aggregation queries it always helps me to visualize what the SQL would be.
In django we now have aggregations.
Here is what I came up with:
models.Annot.objects.select_related().values(
'tag__name','sample__name').annotate(
count_of_users=Count('user__id')).filter(count_of_users__gt=1)
The result set will contain the tag, the sample, and the count of users that tagged said sample with said tag.
Breaking it apart for the folks that are not used to django aggregation:
models.Annot.objects.select_related()
select_related() is forcing all tables related to Annot to be retrieved in the same query
This is what will allow me to specify tag__name and sample__name in the values() call
values('tag__name','sample__name')
values() is limiting the fields to retrieve to tag.name and sample.name
This makes sure that my aggregation on count of clients will group by just these fields
annotate(count_of_users=Count('user__id'))
annotate() adds an aggregation as an extra field to a query
filter(count_of_users__gt=1)
And finally I filter on the aggregate count.
If you want to add an additional filter on what users should be taken into account, you need to do this:
models.Annot.objects.filter(user=[... list of users...]).select_related().values(
'tag__name','sample__name').annotate(
count_of_users=Count('user__id')).filter(count_of_users__gt=1)
I think that is it.
One thing... Notice that I used tag__name and sample__name in the query above. But your models do not specify that tag names and sample names are unique.
Should they be unique? Add a unique=True to the field definitions in the models.
Shouldn't they be unique? You need to replace tag__name and sample__name with tag__id and sample__id in the query above.