I have:
Foo <=> FooGroup <=> Bar
relation, where <=> stands for ManyToManyField.
How do I retrieve all the Foos for a specific Bar instance?
You can .filter(…) [Django-doc] with:
Foo.objects.filter(foogroup__bar=mybar)
with mybar the Bar object for which you want to retrieve the Foos. It is possible that Foo is linked to the same Bar through multiple FooGroups, then it will occur multiple times in the queryset as well. You can use .distinct() [Django-doc] to prevent this:
Foo.objects.filter(foogroup__bar=mybar).distinct()
Related
I have an M2M relationship like this:
class Foo:
# foo fields...
class Bar:
Foos = ManytoManyField(Foo)
I am trying to add a foo to the list of foos attributed to a Bar, so here's what I have:
if Foo not in Bar.Foos:
Bar.Foos.add(Foo)
Question: is the if-judgment really necessary?
Thanks
As stated in the Django docs : https://docs.djangoproject.com/en/2.2/topics/db/examples/many_to_many/
Adding a second time is OK, it will not duplicate the relation
You may call bar.foo.add(baz) many times, it won't create a duplicate relationship or line in database.
p.s.: in Python, everything (variables, instances, etc..) must be lowercase with words separated by underscores. The exceptions are ClassNames, ExceptionNames and GLOBAL_CONSTANT_NAMES. C.f.: What is the naming convention in Python for variable and function names?
Exemple with your above code :
class Bar:
foos = ManytoManyField(Foo)
def your_view(id):
foo = Foo.objects.get(id=id)
bar = Bar.objects.get(foo=foo)
if foo not in bar.foos:
bar.foos.add(foo)
I have three models in play, and want to avoid N+1 query.
class Rule(models.Model):
pass
class RuleConstraint(models.Model):
rules = models.ManyToManyField(Rule)
class Foo(models.Model):
rule = models.ForeignKey(Rule, related_name='foos')
for a given foo, I can get related RuleConstraints like the following
RuleContraint.objects.filter(rules__foos=foo)
Question is, how do I avoid N+1 query symptom, when I have foos instead of a single foo.
ie, is there a better way of doing than
for foo in foos:
rule_constraints = RuleConstraint.objects.filter(rules__foos=foo)
You want prefetch_related
foos = Foo.objects.prefetch_related('rule__rule_constraint')
You can then iterate through the queryset with:
for foo in foos:
rule_constraints = foo.rule.ruleconstraint_set.all()
You can improve this further by using select_related to fetch the rule.
foos = Foo.objects.select_related('rule').prefetch_related('rule__rule_constraint')
For more information see the prefetch related docs - your models are very similar to those in the examples.
Let say, there is object Foo and it has properties Bar.
The properties Bar are unique for an object Foo (each object has its very own properties).
what would be the most efficient way to define such relationship? define foreign key in Foo's model referring to Bar's model? or define one-to-one relation from Bar to Foo (or visa verse)?
django manual says:
A one-to-one relationship. Conceptually, this is similar to a
ForeignKey with unique=True, but the “reverse” side of the relation
will directly return a single object.
this is confusing statement. which relationship to choose for which job? what about efficiency (# of queries)?
thank you.
If you have only one Bar object per Foo then use OneToOne relation. If it can be more than one Bar for single Foo then use ForeignKey.
As for "reverse" statement:
# access to Bar property of Foo in OneToOne relation
foo.bar
# access to first Bar property of Foo in ForeignKeyRelation
foo.bar_set.all().first()
In both cases it will be one SQL query, but OneToOne version looks better :-)
I have a model which is related with other model.
class Foo(...)
...
class Bar(...)
foo = models.ForeignKey(Foo, related_name('bars'))
I need to load all related Bars for many Foos so I use prefetch_related.
Foo.objects.filter(...).prefetch_related('bars')
In debug_toolbar I see additional query which takes Bars for all foos, but there are also queries which takes Bars for every single Foo.
Doesn't prefetch_related work in sqlite? Or am I doing something wrong?
I iterate through all Foos in template, but I think this does not matter.
Ok, the problem was in my code. I used latest() method in my manager which executed another query.
I'm working with a model:
class Foo(models.Model):
name = models.CharField(max_length=255)
bars = models.ManyToManyField('Bar')
In a view I have access to a list of Barobjects and need to get all Foo objects that have any of the Bar objects in their list of bars, so I do this:
foos = Foo.objects.filter(bars__in=list_of_bars)
The issue is that there are duplicates, if a Foo has 2 bars, and both of those bars are in my list_of_bars, which a simple distinct solves:
foos = Foo.objects.distinct().filter(bars__in=list_of_bars)
That's all good and well, except that adding DISTINCT to the query makes it very slow, due to there being 2 million Foo objects in the database.
With all that said, what way(s) can you think of that don't use DISTINCT, but achieve the same result set? If it involves altering models, that's OK.
You could always select uniques in python:
foos = set(Foo.objects.filter(bars__in=list_of_bars))
You need query set. So, maybe (just maybe) this quick nasty hack could help You. It is ugly, but maybe it is fast (not sure about that).
def get_query_set(self):
foos = set(Foo.objects.filter(bars__in=list_of_bars))
return Foo.objects.filter(id__in=[f.id for f in foos])