Django ORM - Filter Related Objects - python

I apologize if this is a duplicate, but I was unable to find any other SO posts that address this matter. I have models like so:
class Person(models.Model):
pass
class Interest(models.Model):
person = models.ForeignKey(Person, related_name='interests')
is_cool = models.BooleanField()
I know that I can find all people who have cool interests like so:
Person.objects.filter(interests__is_cool=True)
However, what I really want is to get only their cool interests when I get the Person object. I know that I could always pluck the related queryset out and operate on it, like so:
interests = person.interests.filter(is_cool=True)
but I cannot assign it back to the person instance since the relationship is reversed. To summarize, the goal is to use the ORM directly to filter the Interest objects being returned in the person.interests queryset.

One possibility is to define a method or property on the model:
def cool_interests(self):
return self.interests.filter(is_cool=True)

Related

Django many to many relation, include all IDs in queryset in both directions

I have 2 models connected via M2M relation
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(B, related_name='papers')
class Author():
name = models.CharField(max_length=70)
Is there a way to include authors as all related authors' IDs (and maybe name somehow)?
Is there a way to include papers IDs as reverse relation (and maybe title as well)?
Author.objects.all().annotate(related_papers=F('papers'))
this only adds id of one paper, first one it finds I think.
Furthermore, changing related_papers to papers gives an error:
ValueError: The annotation ‘papers’ conflicts with a field on the
model.
From what I understand in your comments, you're using DRF. I will give you 2 answers.
1) If you're talking about model serializer, you can use PrimaryKeyRelatedField :
class AuthorSerializer(serializers.ModelSerializer):
papers=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Author
fields = ['name', 'papers']
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
fields = '__all__'
This will return the IDs for the other side of the relationship whether you're on Paper or Author side. That will return the primary keys, not a representation of the object itself.
2) Now you're also talking about performance (e.g. database hit at each iteration).
Django (not DRF-specific) has a queryset method to handle preloading related objects. It's called prefetch_related.
For example, if you know you're going to need the relation object attributes and want to avoid re-querying the database, do as follow:
Author.objects.all().prefetch_related('papers')
# papers will be already loaded, thus won't need another database hit if you iterate over them.
Actually, it has already been implemented for you. You should include a Many-to-Many relationship to author in your Paper model like this:
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(Author, related_name='papers')
That gives you the opportunity to add Author objects to a related set using
p.authors.add(u), assuming that p is the object of Paper model, and a is an object of Author model.
You can access all related authors of a Paper instance using p.authors.all().
You can access all related papers of an Author instance using u.papers.all().
This will return an instance of QuerySet that you can operate on.
See this documentation page to learn more.

How to access data across M2M tables in Django?

What is the 'best practice' way of accessing data across a 1 (or more) many-to-many tables?
This is incredibly difficult for me as I am not sure what I shuld be googling/looking up.
I have attached a diagram of my data model. I am able to query data for 'C' related ot a user, by utilizing serializers.
there has to be a simpler way of doing this (I'm hoping).
Doing it with serializers seems incredibly limiting. I'd like to access a user's 'B' and 'C' and transform the object to only have a custom structure and possible unique values.
Any direction is much appreciated. Pretty new to Django, so I apologize for this newb type of question.
Here is an example of M2M relation using Django:
class User(models.Model):
name = models.CharField(...)
class Song(models.Model)
title = models.CharField(...)
users_that_like_me = models.ManyToManyField('User', ..., related_name='songs_that_i_like')
So a User can like many Songs and a Song can be liked by many Users.
To see all the songs a user liked, we can do:
user = User.objects.get(id='<the-user-id>')
liked_songs = user.songs_that_i_like.all()
And to see all the users who like a particular song we can similarly do:
song = Song.objects.get(id='<the-song-id>')
users_that_like_this_song = song.users_that_like_me.all()
Both liked_songs and users_that_like_this_song are actually querysets, meaning we can do some Django magic on them.
For example, to find all users named Jon that liked this song we can do:
users_that_like_this_song.filter(name='Jon')
We can also add some property shortcuts to our Models to help with some common tasks, for example:
class User(models.Model):
...
#property
def number_of_liked_songs(self):
return self.songs_that_i_like.count()
Then we can do:
user = User.objects.get(id='<the-user-id>')
number_of_songs_i_like = user.number_of_liked_songs
There's much more we can do with Django - if you're looking for something specific let us know.

Django, treat OneToOne related field as my own field

I'm essentially trying to come up with my own inheritance scheme because Django's inheritance doesn't fit my needs.
I'd like parent table(class) hold common data fields.
sub classess would have its own additional data in a separate table.
class ProductBase(models.Model):
common = models.IntegerField()
def get_price(self):
return some_price
class FooProduct(ProductBase):
# no field because I'm proxy
class Meta:
proxy = True
def get_price(self):
return price_using_different_logic
class FooExtra(models.Model):
base = models.OneToOneField(ProductBase, primary_key=True)
phone = models.CharField(max_length=10)
My question is, would it be able to treat as if Foo has FooExtra's fields?
I'd like to do things like following..
foo = FooProduct.objects.create()
foo.phone = "3333" # as django does with its multiple inheritance
foo.save()
FooProduct.objects.filter(phone="3333")
I'd like to list Products of different kind(data)
I need to list them together, so abstract Base inheritance is out
from the list, I'd like to treat each model as polymorphic model, when iterating over ProductBase.objects.all(), product.get_price() will use appropriate classe's method. (without incurring join if don't have to)
When and only when I want, I retrieve the addtional table data (by something like .select_related('fooextra')
Django-polymorphic is close to what I want, but it is rather obscure what it does so I'm afraid to use it, and I think it fails #3.
If I understand well, you want inheritance and you want the fields that are specific to the child class to be on a separate table.
As far as I know, you don't need a proxy class to achieve that, you could just implement multi-table inheritance as specified in the manual at https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance e.g.:
class Base(models.Model):
common = models.IntegerField()
class Foo(Base):
phone = models.CharField(max_length=10)
This, as explained at the link above, will automatically create a one-to-one relationship. And of course you can do foo.phone = "3333" (where foo is of type Foo) as in your example above. And the neat thing is that you can also access foo.common whereas in your example it would have been foo.base.common.
It doesn't seem like you want anything different to Django's standard inheritance.
class ProductBase(models.Model):
common1 = models.IntegerField()
common2 = models.IntegerField()
class FooProduct(ProductBase):
fooextra = models.IntegerField()
class BarProduct(ProductBase):
barextra = models.IntegerField()
If you create instances of each:
foo1 = FooProduct(common1=1, common2=1, fooextra=1)
foo2 = FooProduct(common1=1, common2=1, fooextra=2)
bar1 = BarProduct(common1=1, common2=1, barextra=1)
bar2 = BarProduct(common1=1, common2=1, barextra=2)
You can loop over all products:
for product in ProductBase.objects.all():
print product.common1, product.common2
From a ProductBase object that is actually a FooProduct, you can get the custom field with:
product.foo.fooextra
From a ProductBase object that is actually a BarProduct, you can get the custom field with:
product.bar.barextra
You can still do querying:
foo = FooProduct.objects.get(fooextra=1)
bar = BarProduct.objects.get(barextra=2)
And you can access the common fields directly on those objects:
foo.common1
bar.common2
You can use the InheritanceManager from django-model-utils if you need more control over querying etc - and this should address point 3, too: ProductBase.objects.filter(...).select_subclasses() would give you the FooProduct and BarProduct objects instead of ProductBase objects.

Django filter many-to-many with contains

I am trying to filter a bunch of objects through a many-to-many relation. Because the trigger_roles field may contain multiple entries I tried the contains filter. But as that is designed to be used with strings I'm pretty much helpless how i should filter this relation (you can ignore the values_list() atm.).
This function is attached to the user profile:
def getVisiblePackages(self):
visiblePackages = {}
for product in self.products.all():
moduleDict = {}
for module in product.module_set.all():
pkgList = []
involvedStatus = module.workflow_set.filter(trigger_roles__contains=self.role.id,allowed=True).values_list('current_state', flat=True)
My workflow model looks like this (simplified):
class Workflow(models.Model):
module = models.ForeignKey(Module)
current_state = models.ForeignKey(Status)
next_state = models.ForeignKey(Status)
allowed = models.BooleanField(default=False)
involved_roles = models.ManyToManyField(Role, blank=True, null=True)
trigger_roles = models.ManyToManyField(Role, blank=True, null=True)
Though the solution might be quiet simple, my brain won't tell me.
Thanks for your help.
Have you tried something like this:
module.workflow_set.filter(trigger_roles__in=[self.role], allowed=True)
or just if self.role.id is not a list of pks:
module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
The simplest approach to achieve this would be checking for equalty over the whole instance (instead of the id) in the ManyToManyField. That looks if the instance is inside the many to many relationship. Example:
module.workflow_set.filter(trigger_roles=self.role, allowed=True)
I know this is an old question, but it looks like the OP never quite got the answer he was looking for. If you have two sets of ManyToManyFields you want to compare, the trick is to use the __in operator, not contains. So for example if you have an "Event" model with a ManyToMany to "Group" on field eventgroups, and your User model (obviously) attaches to Group, you can query like this:
Event.objects.filter(eventgroups__in=u.groups.all())
singularity is almost right with the first example. You just need to make sure it's a list. The second example, checking the trigger_roles__id__exact is a better solution though.
module.workflow_set.filter(trigger_roles__in=[self.role.id],allowed=True)

Django model with filterable attributes

I've got two models. One represents a piece of equipment, the other represents a possible attribute the equipment has. Semantically, this might look like:
Equipment: tractor, Attributes: wheels, towing
Equipment: lawnmower, Attributes: wheels, blades
Equipment: hedgetrimmer, Attributes: blades
I want to make queries like,
wheels = Attributes.objects.get(name='wheels')
blades = Attributes.objects.get(name='blades')
Equipment.objects.filter(has_attribute=wheels) \
.exclude(has_attribute=blades)
How can I create Django models to do this?
This seems simple, but I'm just too dense to see the right solution.
One solution that popped into my head is to encode the list of Attribute IDs in an integer list like |109|14|3 and test for attributes using Equipment.objects.filter(attributes_contains='|%d|' % id) -- but this seems really wrong.
Your second example is pretty close, but you need to understand how the QuerySet API works across relationships (i.e. joins).
class Attribute(models.Model):
name = models.CharField(max_length=20)
class Equipment(models.Model):
name = models.CharField(max_length=20)
attributes = models.ManyToManyField(Attribute)
equips = Equipment.objects.filter(
attributes__name='wheels').exclude(attributes__name='blades')
You can use Q objects in your QuerySet to do more interesting queries.
And keep in mind you can always dump the SQL from a QuerySet like this:
print equips.query.as_sql()
Sometimes you'll want to see the exact SQL being generated to make sure you're using the API correctly.

Categories

Resources