Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

django "can't adapt type '__proxy__'" error message

class GenderTypeEnum:
    FEMALE = 1
    MALE = 2
    UNKNOWN = 3
    
    types = (
        (FEMALE, _("Female")),
        (MALE, _("Male")),
        (UNKNOWN, _("Unknown"))
    )

class PersonModel(models.Model):
    identity = models.CharField(max_length=50, unique=True)
    name = models.CharField(max_length=75)
    last_name = models.CharField(max_length=75)
    gender = models.PositiveIntegerField(choices=GenderTypeEnum.types)
    
class StaffModel(models.Model):
    person = models.ForeignKey('PersonModel', on_delete=models.CASCADE, related_name='staffs')
    registration_number = models.CharField(max_length=50, unique=True)
    start_date = models.DateField()
    finish_date = models.DateField(null=True, blank=True)

I am using the following query to list the gender statistics of the staff

StaffModel.objects.values("person__gender").annotate(count=Count("person__gender"))

output:

[
{"person__gender":1, "count":1}, 
{"person_gender":2, "count":5}
]

But gender field is a choices field so that, output that I want like this:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

[
    {"person__gender":1, "gender_exp":"Male", "count":1}, 
    {"person_gender":2, "gender_exp":"Female", "count":5}
]

I created the following class by looking at the answer given to @bachkoi32 Display name of a choice field in Django while using annotate

In order to output, I use this class:

class WithChoices(Case):
    def __init__(self, model, field, condition=None, then=None, **lookups):
        fields = field.split('__')
        for f in fields:
            model = model._meta.get_field(f)

            if model.related_model:
                model = model.related_model

        choices = dict(model.flatchoices)
        whens = [When(**{field: k, 'then': Value(v)}) for k, v in choices.items()]
        return super().__init__(*whens, output_field=CharField())

I changed my query:

qs = StaffModel.objects.values("person__gender").annotate(gender_exp=WithChoices(StaffModel, 'person__gender'), count=Count("person__gender")).values("person__gender","gender_exp","count")

When I want to print the query result, it raise the error;
django.db.utils.ProgrammingError: can’t adapt type ‘proxy

qs = StaffModel.objects.values("person__gender").annotate(gender_exp=WithChoices(StaffModel, 'person__gender'), count=Count("person__gender")).values("person__gender","gender_exp","count")
print(qs)

# raise error;
# django.db.utils.ProgrammingError: can't adapt type '__proxy__'

>Solution :

The labels for your choices are lazy translations, these can’t be passed as values to a query, they need to be converted to strings using force_str

from django.utils.encoding import force_str


class WithChoices(Case):
    def __init__(self, model, field, condition=None, then=None, **lookups):
        fields = field.split('__')
        for f in fields:
            model = model._meta.get_field(f)

            if model.related_model:
                model = model.related_model

        choices = dict(model.flatchoices)
        whens = [When(**{field: k, 'then': Value(force_str(v))}) for k, v in choices.items()]
        return super().__init__(*whens, output_field=CharField())
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading