Closed tkhyn closed 7 years ago
Original comment by Thomas Khyn (Bitbucket: tkhyn, GitHub: tkhyn).
Hi Nick, thanks for the report.
This is an issue you'll have if you use a GenericForeignKey
as well, it's not specifically django-gm2m
related.
Indeed, this message (a bit cryptically I agree) tells you there is no way for django to query the database when it is not explicitly told in which table(s) to look for the data, and that you should instead use a GenericRelation
if you are using a GenericForeignKey
.
In our case, when using django-gm2m
, the end of the message should read: If it is a GM2MField, consider using the automatically created GM2MRelation.
Concretely, that means that instead of doing:
class Option(models.Model):
....
class Main(models.Model):
options = GM2MField()
option = Option.objects.create()
mains_with_option = Main.objects.filter(options=option)
You should instead do:
class Option(models.Model):
....
class Main(models.Model):
# adding Option creates the reverse relation from Option to Main, called main_set
options = GM2MField(Option)
option = Option.objects.create()
mains_with_option = option.main_set.all()
And you'll have the expected result.
With the second method, Django knows in which tables to look for the data (Option's and Main's), starting from Option's. In the first case, it knew it had to start from Main's table, and had no explicit indication where to look for the related object.
There is really not much I can do on my side to enable this feature, as this is a django limitation with generic relations. What I can do is add a § in the documentation warnings about this, as the django error message is misleading for django-gm2m
users.
EDIT: typo
Original comment by Nick Guziy (Bitbucket: [Nick Guzy](https://bitbucket.org/Nick Guzy), ).
Thank you. It's clear now that I should use
#!python
mains_with_option = option.main_set.all()
if I need all mains with selected option. But what should I do if I need to get all instances of Main model that have options = [option1, option2] if I can't do
#!python
mains_with_options = Main.objects.filter(options=[option1, option2])
Original comment by Thomas Khyn (Bitbucket: tkhyn, GitHub: tkhyn).
Well you'd simply need to do a normal for
loop:
mains_with_options = set()
for option in [option1, option2]:
mains_with_options.update(option.main_set.all())
This 'looks' inefficient (as many queries as passed options), but even it it were possible to do Main.objects.filter(options__in=[option1, option2])
it would have to do exactly the same thing (one query for each passed option) ...
EDIT: using set
instead of []
to avoid duplicates in the result
Hello. The following goal:
mains_with_options = Main.objects.filter(options=[option1, option2])
can be also achieved this way:
option_ct = ContentType.objects.get_for_model(Option)
im = create_gm2m_intermediary_model(getattr(Main, 'options').field, Main)
ids_of_mains_with_options = set(im.objects.filter(gm2m_ct_id=option_ct.id, gm2m_pk__in=[option1.pk, option2.pk]).values_list('gm2m_src_id', flat=True))
mains_with_options = Main.objects.filter(id__in=ids_of_mains_with_options)
Original report by Nick Guziy (Bitbucket: [Nick Guzy](https://bitbucket.org/Nick Guzy), ).
Hi Thomas! I can't figure out if I can filter model with GM2MField by its value, like I can do with django's GenericForeignKey.
Here is my case:
what I want to achieve is to get all instances of Main model, that have option in the options GM2MField i.e.
this code raises Exception
Can you please help me? Should I manually add a relation (from gm2m/relations.py) to Option class or am I making invalid lookup?