Используйте 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(),
    ),
)
Другие вопросы по тегам