How to get ManyToMany Field values in with selecte_related - python

I'm new to django. I'm using a ManyToMany field in my Profile model with Membership Model.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
picture = models.ImageField(blank=True)
membership = models.ManyToManyField(MemberShip, null=True)
What I want to do is I want to get all the users who has specific membership(from membership model)
For example I want the list of all the users who has the Red Membership. I researched and found out that select_related() or prefetch_related can help in some way. but I can't understand how can I use these methods to get what I want.
I've tried this:
user_memberships_all = Profile.objects.all()
for m in user_memberships_all:
affiliate_with_membership = m.membership.select_related('user').all()
but I get an error saying Invalid field name(s) given in select_related: 'user'.
if there is another of achieving this. can you please help with that. Thank You.

To get all Users related to a specific MemberShip you can follow the OneToOneField backwards and then filter on the ManyToManyField
# Filtering on a "name" attribute of MemberShip
User.objects.filter(profile__membership__name='red')
# Using a MemberShip instance
red_membership = MemberShip.objects.get(name='red')
User.objects.filter(profile__membership=red_membership)

Related

How to see all possible query sets of a model in django

I have two models like below in django
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(....)
last_name = models.CharField(_(....)
email = models.EmailField(...)
class VcsToken(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
as you can see VcsToken is related to User, and a user can have many VcsToken,
So how do I get all the VcsTokens of a User.
Similarly I have many one to many relationships from user to other models, so How do I know their reference name? (I know its a set but how do I know the set name? ) Is there any way to list the query set names for a model.
What you are looking for is the feature for walking backward on the foreign key relationships. This is covered here in the docs: https://docs.djangoproject.com/en/3.0/topics/db/queries/#following-relationships-backward
In your example you should be able to access VcsToken from User like this:
user = User.objects.get(pk=1) # let's get an example user
user.vcstoken_set.all() # returns all related VcsToken objects.
Optionally, when the ForeignKey is defined, you can specify a related_name argument that would be used by django for this purpose. For example:
class User(AbstractBaseUser, PermissionsMixin):
...
email = models.EmailField(...)
class VcsToken(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tokens')
If such an argument is specified, then that is the reverse lookup name used by django and you would need to do user.tokens.all() to access them. When no such argument is specified a default name is used by django that ends with "_set".
Hope this helps, let me know if anything needs to be clarified.
For a given User object myuser, you can access this with:
myuser.vcstoken_set.all()
so How do I know their reference name?
This is the value for the related_name=… parameter [Django-doc] if there is no such parameter in the ForeignKey construction, it will default to modelname_set, with the modelname in lowercase.
Is there any way to list the query set names for a model.
You can access all ManyToOneFields with:
django.db.models.fields.reverse_related.ManyToOneRel
[f.get_accessor_name() for f in User._meta.get_fields() if isinstance(f, ManyToOneRel)]
This will construct a list of the names of ForeignKeys in reverse.

How to check if ManyToMany field is not empty?

How do I check if there are any ManyToMany field objects related to my model object?
For example, I have a model:
class Category(models.Model):
related_categories = models.ManyToManyField('self', blank=True)
I want to do something only if there are related objects existing:
if example_category.related_categories:
do_something()
I tried to do example_category.related_categories, example_category.related_categories.all(), example_category.related_categories.all().exists(), example_category.related_categories.count(), but none of these works for me.
I have no any additional conditions to filter by.
Is there any easy way to check emptiness of this field?
you should use the .exists method:
related_categories = example_category.related_categories
if related_categories.exists():
# do something

Django - Get objects that don't have one to to one attribute

I have a concept where there is a model that has a OneToOne field with User. How can I query for all users that are not assigned to one of these?
For example:
class SpecialUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
Not all users are special users. How can I get all users that are not special users using objects (ie: User.objects.filter(something)).
Thanks!
You can do the following:
User.objects.filter(specialuser=None)
Or:
User.objects.filter(specialuser__isnull=True)
You can provide related_name to make it more readable or to avoid pottential collisions. Example:
class SpecialUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="special_user_profile")
In this case, you'd use the related name instead:
User.objects.filter(special_user_profile=None)

Filter related model django

I am building the example django polls application. I want to exclude all polls, which have no choices. For that I have to access the related choices objects:
return Question.objects.filter(
pub_date__lte=timezone.now()
).exclude(
choice_set__count=0
).order_by('-pub_date')[:5]
But this query results in a field error:
Cannot resolve keyword 'choice_set' into field. Choices are: choice, id, pub_date, question_text
How can I query related models from the query?
To filter against a related model, you just use the lower-case model name - you can see that choice is one of the available fields.
However, this still won't work; there is no __count attribute to filter against. You can add one by using annotations, but there is a simpler way: compare against None:
.exclude(choice=None)
In the Choice model set the related_name to the Question foreign key.
Example:
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='choices')
# other code..
And then your query should be something like that:
return (Question.objects
.filter(pub_date__lte=timezone.now(),
choices__isnull=False)
.order_by('-pub_date')[:5])
Note: There is no `__count' lookup. If you want to rely on counts then check the docs on this.
docs: https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_name
The _set only applies when you are using that to retrieve the related model outside of the queryset, instead, as the error suggests, you can just use choice
.exclude(choice__isnull=True)

How to perform queries in Django following double-join relationships (or: How to get around Django's restrictions on ManyToMany "through" models?)

There must be a way to do this query through the ORM, but I'm not seeing it.
The Setup
Here's what I'm modelling: one Tenant can occupy multiple rooms and one User can own multiple rooms. So Rooms have an FK to Tenant and an FK to User. Rooms are also maintained by a (possibly distinct) User.
That is, I have these (simplified) models:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User)
maintainer = models.ForeignKey(User)
tenant = models.ForeignKey(Tenant)
The Problem
Given a Tenant, I want the Users owning a room which they occupy.
The relevant SQL query would be:
SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;
Getting any individual value off the related User objects can be done with, for example, my_tenant.rooms.values_list('owner__email', flat=True), but getting a full queryset of Users is tripping me up.
Normally one way to solve it would be to set up a ManyToMany field on my Tenant model pointing at User with TenantRoom as the 'through' model. That won't work in this case, though, because the TenantRoom model has a second (unrelated) ForeignKey to User(see "restictions"). Plus it seems like needless clutter on the Tenant model.
Doing my_tenant.rooms.values_list('user', flat=True) gets me close, but returns a ValuesListQuerySet of user IDs rather than a queryset of the actual User objects.
The Question
So: is there a way to get a queryset of the actual model instances, through the ORM, using just one query?
Edit
If there is, in fact, no way to do this directly in one query through the ORM, what is the best (some combination of most performant, most idiomatic, most readable, etc.) way to accomplish what I'm looking for? Here are the options I see:
Subselect
users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
Subselect through Python (see Performance considerations for reasoning behind this)
user_ids = id__in=my_tenant.rooms.values_list('user')
users = User.objects.filter(id__in=list(user_ids))
Raw SQL:
User.objects.all("""SELECT auth_user.*
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id""")
Others...?
The proper way to do this is with related_name:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User, related_name='owns')
maintainer = models.ForeignKey(User, related_name='maintains')
tenant = models.ForeignKey(Tenant)
Then you can do this:
jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)
User.objects.filter(owns__tenant=bob)

Categories

Resources