简体   繁体   中英

Use annotate and Case with different field types in Django

I have a model with those field:

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)

I want to make a query on that model using annotate and Case with those rules:

  • When parent is null it returns the text_code field
  • When parent is not null it returns parent__number field

I can make it with SQL in PostgreSQL like:

CASE WHEN "ModelA"."parent_id" IS NOT NULL 
    THEN T3."number"**::integer** 
ELSE "ModelA"."text_code"**::integer** END AS "control_field" 

But I can't make that with Django ORM. I've tried the following:

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(),
    ),
)

But when I call query result, it rase the following error:

django.db.utils.ProgrammingError: CASE types character varying and integer cannot be matched

I know it is an Database error, because the SQL result from django construct doesn't have the ::integer add.

CASE WHEN "ModelA"."parent_id" IS NOT NULL 
    THEN T3."number" 
ELSE "ModelA"."text_code" END AS "control_field"

How can I solve this? I'm working with Django 1.11

The answer is, use 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(),
    ),
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM