Объединение Django F, Value и слова для аннотирования набора запросов

У меня есть сценарий, в котором я хочу аннотировать набор запросов внешне подготовленными данными в диктовке. Я хочу сделать что-то вроде следующего:

value_dict = {"model1": 123.4, "model2": 567.8}
qs = ModelClass.objects.annotate(
    value=Value(value_dict.get(F('model__code'), 0))
)

Результаты в настоящее время показывают все как 0, так как F(), кажется, не является лучшим способом поиска разборов, так как он не возвращает строку и разрешается дальше по дорожке.

Ваша помощь и предложения будут высоко оценены

Я в настоящее время на Python 3.6 и Django 1.11

2 ответа

Решение

Самое близкое, что я дошел до этого, - это циклическое прохождение через dict и объединение целой группы отфильтрованных наборов запросов на основе ключей dict. Вот так:

value_dict = {"model1": 123.4, "model2": 567.8}
array_of_qs = []
for k, v in value_dict.items():
    array_of_qs.append(
        ModelClass.objects.filter(model__code=k).annotate(value=Value(v))
    )
qs = array_of_qs[0].union(*array_of_qs[1:])

Тогда у qs будет результат, который я хочу, это значения dict, основанные на ключах dict, аннотированных для каждого экземпляра.

Прелесть этого в том, что есть только один вызов БД, когда я решаю использовать qs, за исключением того, что весь этот процесс не касается БД из того, что я понимаю.

Не уверен, что есть лучший способ, но подождем, чтобы увидеть, есть ли другие ответы, прежде чем принять этот.

НОВЫЙ УЛУЧШЕННЫЙ МЕТОД РАБОТЫ НИЖЕ

К сожалению, вышеприведенное не работает с values_list или значениями текущей версии 1.11 Django из-за этой проблемы: https://code.djangoproject.com/ticket/28900

Я разработал более чистый способ ниже:

value_dict = {"model1": 123.4, "model2": 567.8}
whens = [
    When(model__code=k, then=v) for k, v in value_dict.items()
]
qs = ModelClass.objects.all().annotate(
    value=Case(
        *whens,
        default=0,
        output_field=DecimalField()
    )
)

Надеюсь, это поможет кому-то

Неспособность сделать это раздражала меня уже много лет.

Основываясь на принятом ответе, я разработал общий QuerySet:

      def add_from_dict_value(self, reference_dict, fieldname_used_as_key, annotation_name_to_generate, output_field=CharField(), default=None)->:
    """
    enables annotations with values coming from a dictionary

    Use:
        def add_highlight(self)->models.QuerySet:
            return self.add_from_dict_value(MY_CONVERSION_DICTIONARY, 'rating', 'highlight')
    """ 

    whens = [
        When(**{fieldname_used_as_key:k, 'then':Value(v)}) for k, v in reference_dict.items()
    ]
    return self.annotate(**{
        annotation_name_to_generate : Case(
            *whens,
            default=default,
            output_field=output_field
        )}
    )
Другие вопросы по тегам