Используйте annotate и Case с различными типами полей в Django
У меня есть модель с этими полями:
class ModelA(models.Model):
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.PROTECT, related_name='child')
number = models.PositiveIntegerField()
text_code = models.CharField(max_length=255, blank=True, null=True)
Я хочу сделать запрос по этой модели, используя annotate и Case с этими правилами:
- Когда parent равен нулю, он возвращает поле text_code
- Когда parent не равен NULL, он возвращает поле parent__number
Я могу сделать это с SQL в PostgreSQL, как:
CASE WHEN "ModelA"."parent_id" IS NOT NULL
THEN T3."number"**::integer**
ELSE "ModelA"."text_code"**::integer** END AS "control_field"
Но я не могу сделать это с Django ORM. Я пробовал следующее:
query = ModelA.objects.all().select_related('parent').annotate(
control_field=Case(
When(parent_id__isnull=False, then='parent__number'),
default='text_code',
output_field=IntegerField(),
),
)
Но когда я вызываю результат запроса, он вызывает следующую ошибку:
django.db.utils.ProgrammingError: CASE типы символов различаются, и целое не может быть сопоставлено
Я знаю, что это ошибка базы данных, потому что результат SQL из конструкции django не имеет добавления :: integer.
CASE WHEN "ModelA"."parent_id" IS NOT NULL
THEN T3."number"
ELSE "ModelA"."text_code" END AS "control_field"
Как я могу решить это? Я работаю с Django 1.11
1 ответ
Ответ: используйте Cast:
query = ModelA.objects.all().select_related('parent').annotate(
control_field=Case(
When(parent_id__isnull=False, then=Cast('parent__number', IntegerField())),
default=Cast('text_code', IntegerField()),
output_field=IntegerField(),
),
)