[Django]-Django get choice display on using .values

2👍

You can’t do that. values is a built in django queryset method which is used to get dictionaries of data instead of model instances you can read more about it here.

The conventional (and proper) way of attaching choices with model for a field is using static variable like this.

class MyModel(models.Model):
    TYPE_CHOICES = (
        # (<DB VALUE>, <DISPLAY_VALUE>)
        ('a', 'Choice A'),
        ('b', 'Choice B'),
    )

    type = models.CharField(max_length=1, choices=TYPE_CHOICES)

You can access choices for type field outside model like this.

MyModel.TYPE_CHOICES

2👍

Where I do call .values of choice fields into my queryset I deal with this in the following way:

Assume the following model

from enum import Enum

class TypeChoice(Enum):
    a = 'class A'
    b = 'class B'

class MyModel(models.Model):
    type = models.CharField(max_length=1, choices=[(tag.name,tag.value) for tag in TypeChoice])

Using the query my_qset = MyModel.objects.values('type') the display values are available as:

for item in my_qset:
     print(TypeChoice[item].value)

To deal with this in my templates I write a custom template filter, say type_display:

from django import template
import TypeChoice
register = template.Library()

@register.filter
def type_display(var):
    return TypeChoice[var].value
👤GCru

1👍

I had a similar need and unfortunately you can’t do so with only values; however, you can do something similar with some crafty annotations and django’s custom enums (IntegerChoices/TextChoices) classes.

I created this from another SO question but I can’t remember where I got the inspiration from. Basically, you can pass in the corresponding model or the choices you’d like to map back to labels via annotations on the queryset.

class ChoicesLabelCase(Case):
    def __init__(self, field: str, model: Model = None, choices: list = None, *args, **kwargs) -> None:
        if choices is None and model is None:
            raise ValueError("Either a model or a choices parameter must be provided.")
        elif choices is None:
            choices = model._meta.get_field(field).flatchoices

        cases_list = [When(**{field: val, "then": Value(label)}) for (val, label) in choices]
        super(ChoicesLabelCase, self).__init__(*cases_list, output_field=CharField(), *args, **kwargs)

As an example take this model:

class FruitType(models.IntegerChoices):
    APPLE = 1, 'Apple'
    BANANA = 2, 'Banana'
    ORANGE = 3, "Orange"

class Fruit(models.Model):
    type = models.IntegerField(choices=FruitType.choices)

You can annotate the labels like so:

>>> Fruit.objects.all().annotate(
    type_label=ChoicesLabelCase('type', FruitType)
).values("type", "type_label")

[
    {'type': 1, 'type_label': 'Apple'}, 
    {'type': 2, 'type_label': 'Banana'}, 
    ...
]
👤msf

0👍

This code example maybee help you:

 alertsfilter = list(Alerte.objects.filter(viewed__isnull=False).values(
    'id', 
    'type', 
    'created_at', 
    'viewed',
    'sent', 
    'agent_id', 
    'geofence_id', 
    'objet__id', 
    'objet__nom',
    'objet__marker',
    'geofence__id'))

for alert in alertsfilter:
    # alert_type = alert['type']
    alert['type'] = Alerte.objects.get(pk=alert['id']).get_type_display()

Leave a comment