martsberger / django-pivot

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

Pivot returns List instead of ValuesQueryset #33

Open Gogicool-geek opened 11 months ago

Gogicool-geek commented 11 months ago

Hello! I the revision 1.9.0 Pivot returned ValuesQueryset, but in the revision 1.10.0 it returns List:

#rev 1.9.0
..
values_list = queryset.values(*values).annotate(**annotations)
..

#rev 1.10.0
..
values_list = [_swap_dictionary_keys(result, column_alias_map)
                   for result in queryset.values(*values).annotate(**annotations)]
..

Is it posible to return Queryset again?

martsberger commented 11 months ago

This was deliberate in order to support column names that django no longer allows in the queryset.

What is your use case for needing a ValuesQueryset instead of a list?

Gogicool-geek commented 11 months ago

I am using delievered ValuesQueryset in view with django_filter.

# filters.py
class UserGroupListFilter(django_filters.FilterSet):
    """Filter User by user name and group name"""

    name = django_filters.ModelChoiceFilter(
        queryset=User.objects.filter(is_active=True), widget=get_user_autocomplete_widget()
    )
    groups = django_filters.ModelMultipleChoiceFilter(queryset=Group.objects.all())

# views.py
class UsersGroupsPivotView(UserHasSubordinatesMixin, TMSBaseTableView):
    """User & goups pivot table"""

    table_class = UsersGroupsTable
    filterset_class = UserGroupListFilter
    template_name = "layout_list.html"

    def get_queryset(self):
       pivot_table = pivot( subordinates.filter(is_active=True).order_by("name"),
          ["id", "name"],
          "groups__name",
          "pk",
          aggregation=Count,
      )
      return pivot_table

When I upgrade django-pivot up to 1.10 the app comes with error:

Traceback (most recent call last):
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python3.10/contextlib.py", line 79, in inner
    return func(*args, **kwds)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/contrib/auth/mixins.py", line 71, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/contrib/auth/mixins.py", line 128, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/home/gogicool/dev/tms/tms/common/views.py", line 99, in dispatch
    responce = super().dispatch(*args, **kwargs)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django_filters/views.py", line 74, in get
    self.filterset = self.get_filterset(filterset_class)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django_filters/views.py", line 38, in get_filterset
    return filterset_class(**kwargs)
  File "/home/gogicool/dev/tms/venv/lib/python3.10/site-packages/django_filters/filterset.py", line 176, in __init__
    model = queryset.model
AttributeError: 'list' object has no attribute 'model'
geoffrey-eisenbarth commented 2 months ago

Just ran into this myself. In my case, I was using pivot() to unstack a date column and then using those values in a QuerySet.annotate() call that calculated compound annual growth rates based on the new columns. This is no longer possible since pivot() is returning a list. Simple example:

My data looks similar to this:

region indicator date value
10180  EMP       2001 [...]
10180  RGP       2001 [...] 
10180  EMP       2010 [...]
10180  RGP       2010 [...]
10180  EMP       2020 [...]
10180  RGP       2020 [...]
[...]
19010  EMP       2001 [...]
19010  RGP       2001 [...]
19010  EMP       2010 [...]
19010  RGP       2010 [...]
19010  EMP       2020 [...]
19010  RGP       2020 [...]

etc for, say 20 regions, two indicators and three dates.

I then run this QuerySet into

pivot(
  qs,
  rows=['region', 'indicator'],
  column='date',
  data='value'
).annotate(
  short_term_cagr=(F('2010') / F('2001')) ** (1 / Value(10.0)) - 1,
  long_term_cagr=(F('2020') / F('2001')) ** (1 / Value(20.0)) - 1,
  region_name=Subquery([...]),
  indicator_name=Subquery([...]),
).values(
  'region_name', 'indicator_name', 'short_term_cagr', 'long_term_cagr',
)

I think it's very useful to be able to annotate a pivoted table. If it seems like it's still possible to support ValuesQuerySet I would be willing to try to put together a PR if I could get some guidance.