Search code examples
djangomany-to-manydjango-ormdjango-annotate

Django annotate single instance of many-2-many field using 'when' on other fields of the instnce


I have a somewhat complicated model, so I will do my best to give an example that simplifies my current state, and my need.

I have a queryset:

qs = MyModel.objects.all()

Each instance in this queryset, has a many-2-many field to another model, let's call it 'First_M2M'. First_M2M has a foreign key to another model, and a many-2-many to yet another model (FkModel and Second_M2M, respectively):

qs[0].first_m2m.fk_model.name  # This is a string.
qs[0].first_m2m.second_m2m.all()  # This is a many2many manager.

The Second_M2M has another many-2-many relationship, Third_M2M:

qs[0].first_m2m.second_m2m[0].third_m2m.all() # Also a m2m manager.

Now that's what I'm trying to do: I want to order my qs, based on a value from one of the second_m2m instances. However, I need to choose which instance is it, and this is done by querying a field in the fk_model (to determine the first_m2m instance) AND a field in one of the instances in the third_m2m (this will determine the second_m2m).

In order to make it even more interesting, the value to order by, is YAML.

Here's what I tried to do:

qs.annotate(val_to_filter_by=Case(
                              When(
                               first_m2m__fk_model__name='foo',
                               first_m2m__second_m2m__third_m2m__some_field='bar'),
                              then='first_m2m__second_m2m__value_field',
                              default=Value(None),
                              output_field=YAMLField()
                              )
                             ).order_by(val_to_filter)

I believe what I got wrong is the querying, that is not coherent enough for Django to determine which instance it should take. But I can't find my problem.

Any help will be much appreciated.


Solution

  • Solved it...

    I had a mistake with my 'then. It was part of my 'Case' instead of the 'When'. Here is the solution:

    qs.annotate(val_to_filter=Case(
                                When(
                                    first_m2m__fk_model__name='foo',
                                    first_m2m__second_m2m__third_m2m__some_field='bar',
                                    then=F('first_m2m__second_m2m__value_field')
                                    ),
                                   default=Value(''),
                                   output_field=YAMLField()
                                   )).order_by(val_to_filter)
    

    UPDATE: Didn't solve it...

    Although I got the right query, and it worked, I am getting ALL instances of the second_m2m, instead of a single instance. Still not sure how to get exactly what I need, seems like Django is not my friend in this case.

    UPDATE2: Changed 'default=None' and added

    .exclude(val_to_filter=None)
    

    right before the ordering. Seems to work...