Объединение 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
)}
)