martsberger / django-pivot

A module for pivoting Django Querysets
MIT License
210 stars 16 forks source link

TypeError: annotate() keywords must be strings + FIX #20

Closed corneliusabel closed 1 year ago

corneliusabel commented 3 years ago

First of all, thanks for the awesome package! Its really usefull!

I found a little bug in the pivot.py line 52. When i run pivot with the following parameters (models below):

pivot(Participant.objects.all(), 'language__name','languagelevel__level','id',Count)

I get the following error:

File "/django_pivot/pivot.py", line 41, in pivot
    values_list = queryset.values(*values).annotate(**annotations)
TypeError: annotate() keywords must be strings

This can be fixed by explicitly casting strings as keys for the returned dictionary:

def _get_annotations(column, column_values, data, aggregation, display_transform=lambda s: s, default=None):
    value = data if hasattr(data, 'resolve_expression') else F(data)
    return {
        str(display_transform(display_value)): Coalesce(aggregation(Case(When(Q(**{column: column_value}), then=value))), default)
        for column_value, display_value in column_values
    }

Should this be fixed in the code base or did I do something else wrong?

Best,

Cornelius


Relevant Models

class Participant(models.Model, ParticipantMailContextMixin):
    language = models.ManyToManyField(Language, through='LanguageLevel')

class LanguageLevel (models.Model):
    language = models.ForeignKey(Language, on_delete=models.CASCADE, verbose_name=_('Language'))
    participant = models.ForeignKey(Participant, on_delete=models.CASCADE)
    level = models.IntegerField(choices=LANGUAGELEVELCHOICES, blank=True, null=True, verbose_name=_('Level'))

class Language (models.Model):
    name = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('Language'))
martsberger commented 1 year ago

This is why the display_transform hook exists in the pivot function. Using str fixes your issue, but I don't know that it is always the right thing to do, so you can call pivot this way:

pivot(Participant.objects.all(), 'language__name','languagelevel__level','id',Count, display_transform=str)
martsberger commented 1 year ago

I'm going to close this issue. If the above suggestion to use display_transform does not work for you, open a new issue.