2

I need to annotate a queryset with strings from dictionary. The dictionary keys come from the model's field called 'field_name'.

I can easily annotate with strings from dictionary using the Value operator:

q = MyModel.objects.annotate(
  new_value=Value(value_dict[key], output_field=CharField()))

And I can get the field value from the model with F expression:

q = MyModel.objects.annotate(new_value=F('field_name'))

Putting them together however fails:

# doesn't work, throws
# KeyError: F(field_name)    
q = MyModel.objects.annotate(
  new_value=Value(value_dict[F('field_name')],
                  output_field=CharField()))

Found this question, which afaiu tries to do the same thing but that solution throws another error:

Unsupported lookup 'field_name' for CharField or join on the field not permitted.

I feel like I'm missing something really obvious here but I just can't get it to work. Any help appreciated.

dfrankow
  • 20,191
  • 41
  • 152
  • 214
  • Well a dictionary does not understand a Django `F` value, since Python evaluates `value_dict[F('field_name')]`, that thus means it raises an error. You *can* do this by using a `Case(When(..))` ssequence, but I really doubt if this will be an improvement. – Willem Van Onsem Oct 15 '18 at 14:53
  • `Case(When(` is the idea behind the solution in the linked question. Couldn't get that to work either.. – runs on clozapine Oct 15 '18 at 14:58

1 Answers1

4

Right, just as I thought, a tiny piece was missing. The Case(When(... solution in the linked question worked, I just needed to wrap the dictionary value in Value() operator as follows:

qs = MyModel.objects.annotate(
    new_value=Case(
        *[ When(field_name=k, then=Value(v)) for k,v in value_dict.items() ],
        output_field=CharField()
    )
)