I'm having some trouble working out the best way to do queries with one to many relationships in Django. Best explained by an example:
class Item(models.Model):
name = models.CharField(max_length=30)
class Attribute(models.Model):
item = models.ForeignKey(Item)
name = models.CharField(max_length=30)
Items can have multiple attributes. Lets say the attribute is specific to an item though so ManyToMany is not appropriate here. How would I find all items with an attribute with name=a1 but also have an attribute with name=a2?
Something like this:
a1_objects = Attribute.objects.filter(name="a1").values("item__id")
a2_objects = Attribute.objects.filter(name="a2").values("item__id")
#Take the intersection (does this method of taking an intersection work?)
ids_with_a1_and_a2 = [id for id in a1_objects if id in a2_objects]
#Get item objects with those ids
results = Item.objects.filter(id__in = ids_with_a1_and_a2)
Surely there is a better way than my suggested approach? It doesn't seem efficient to me.
Check this section in the docs: Spanning multi-valued relationships
Unless I miss something, filtering Item should work:
Item.objects.filter(attribute_name="a1").filter(attribute__name="a2")
Related
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)
In my Django app I have two model and I don't know how to do a query for select the right record. This is the code:
class tab1 (models.Model):
id_tab1 = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
class tab2 (models.Model):
id_tab1 = models.ForeignKey(tab1)
type = models.IntegerField()
I would like to select the tab1 records that have tab2.type equal to some condition. How can I do this in Django?
your_queryset = tab1.objects.filter(tab2__type=value)
See the relevant documentation here
In a few words: you can span relationships either way (i.e. from each end of a foreign key).
The condition is specified in the named argument to filter(). The one I suggested above is the simplest one (i.e. equality), but there are quite a few more (e.g. startswith, contains, etc). Please read here
Consider you have values 1,2 stored for the field type. The following illustrates the one way of achieving your need for type=1.
filtered_objs = tab1.objects.filter(type=1)
tab2.objects.filter( tab1__in=filtered_objs)
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)
Here is my simplified model:
class Item(models.Model):
pass
class TrackingPoint(models.Model):
item = models.ForeignKey(Item)
created = models.DateField()
data = models.IntegerField()
class Meta:
unique_together = ('item', 'created')
In many parts of my application I need to retrieve a set of Item's and annotate each item with data field from latest TrackingPoint from each item ordered by created field. For example, instance i1 of class Item has 3 TrackingPoint's:
tp1 = TrackingPoint(item=i1, created=date(2010,5,15), data=23)
tp2 = TrackingPoint(item=i1, created=date(2010,5,14), data=21)
tp3 = TrackingPoint(item=i1, created=date(2010,5,12), data=120)
I need a query to retrieve i1 instance annotated with tp1.data field value as tp1 is the latest tracking point ordered by created field. That query should also return Item's that don't have any TrackingPoint's at all. If possible I prefer not to use QuerySet's extra method to do this.
That's what I tried so far... and failed :(
Item.objects.annotate(max_created=Max('trackingpoint__created'),
data=Avg('trackingpoint__data')).filter(trackingpoint__created=F('max_created'))
Any ideas?
Here's a single query that will provide (TrackingPoint, Item)-pairs:
TrackingPoint.objects.annotate(max=Max('item__trackingpoint__created')).filter(max=F('created')).select_related('item').order_by('created')
You would have to query for items without TrackingPoints separately.
This isn't directly answer to your question, but in case don't need exactly what you described you might be interested in greatest-n-per-group solution. You can take a look on my answer on similar question:
Django Query That Get Most Recent Objects From Different Categories
-- this should apply directly to your case:
items = Item.objects.annotate(tracking_point_created=Max('trackingpoint__created'))
trackingpoints = TrackingPoint.objects.filter(created__in=[b.tracking_point_created for b in items])
Note that second line can produce ambiguous results if created dates repeat in TrackingPoint model.
In Django, I have two models:
class Product(models.Model):
name = models.CharField(max_length = 50)
categories = models.ManyToManyField(Category)
class ProductRank(models.Model):
product = models.ForeignKey(Product)
rank = models.IntegerField(default = 0)
I put the rank into a separate table because every view of a page will cause the rank to change and I was worried that all these writes would make my other (mostly read) queries slow down.
I gather a list of Products from a simple query:
cat = Category.objects.get(pk = 1)
products = Product.objects.filter(categories = cat)
I would now like to get all the ranks for these products. I would prefer to do it all in one go (using a SQL join) and was wondering how to express that using Django's query mechanism.
What is the right way to do this in Django?
This can be done in Django, but you will need to restructure your models a little bit differently:
class Product(models.Model):
name = models.CharField(max_length=50)
product_rank = models.OneToOneField('ProductRank')
class ProductRank(models.Model):
rank = models.IntegerField(default=0)
Now, when fetching Product objects, you can following the one-to-one relationship in one query using the select_related() method:
Product.objects.filter([...]).select_related()
This will produce one query that fetches product ranks using a join:
SELECT "example_product"."id", "example_product"."name", "example_product"."product_rank_id", "example_productrank"."id", "example_productrank"."rank" FROM "example_product" INNER JOIN "example_productrank" ON ("example_product"."product_rank_id" = "example_productrank"."id")
I had to move the relationship field between Product and ProductRank to the Product model because it looks like select_related() follows foreign keys in one direction only.
I haven't checked but:
products = Product.objects.filter(categories__pk=1).select_related()
Should grab every instance.
For Django 2.1
From documentation
This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
Entry.objects.filter(blog__name='Beatles Blog')
Doc URL
https://docs.djangoproject.com/en/2.1/topics/db/queries/
Add a call to the QuerySet's select_related() method, though I'm not positive that grabs references in both directions, it is the most likely answer.