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)
Related
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()
I have 2 models which have foreign keys referencing a third model. Its something like this:
class Foo(models.Model):
...
class Model1(models.Model):
foo = models.ForeignKey(Foo, related_name='m1_related')
class Model2(models.Model):
foo = models.ForeignKey(Foo, related_name='m2_related')
Now, lets say I have an instance of Foo and a string which is either 'm1' or 'm2'. How can I use this string to call the appropriate queryset using the related names?
Something like this:
my_str = 'm1'
foo.my_str+'_related'.objects.all()
Obviously, the above code is just wrong in so many ways, but hopefully that makes it clear. I don't want to use if else conditions for this, as the number of models will be a lot, and all their related names will follow the same pattern.
Thanks.
You can use getattr(..) to obtain the name of an attribute, like:
my_str = 'm1'
getattr(foo, my_str+'_related').objects.all()
A call to getattr(x, 'y') is equivalent to x.y (note that in getattr(..) the 'y' is a string. So we can use getattr(..) obtain the attribute.
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.
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])