Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using a renderer in DRF settings for Excel output option #28

Closed
FlipperPA opened this issue Sep 5, 2017 · 5 comments
Closed

Using a renderer in DRF settings for Excel output option #28

FlipperPA opened this issue Sep 5, 2017 · 5 comments

Comments

@FlipperPA
Copy link

FlipperPA commented Sep 5, 2017

I'm trying to create an option on all of my endpoints to output XLS files when the appropriate header is set. I may not be understanding correctly, but here's what I've tried to do:

REST_FRAMEWORK = {
    ...
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
        'rest_pandas.renderers.PandasExcelRenderer',
    ),
}

I haven't included it in INSTALLED_APPS as I've read about the problems that causes. However, when trying this method, Django's runserver is giving me this error:

ImportError: Could not import 'rest_pandas.renderers.PandasExcelRenderer' for API setting 'DEFAULT_RENDERER_CLASSES'. ImportError: cannot import name 'APIView'.

When I try this, runserver comes up but the OPTIONS only shows application/json and text/html under the renders section:

from rest_pandas.renderers import PandasExcelRenderer

REST_FRAMEWORK = {
...
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
        PandasExcelRenderer,
    ),
}

I'm sure I'm missing something simple, and I'll be happy to issue a how-to PR on the README if someone can help me get this working! :)

@sheppard
Copy link
Member

This use case wasn't fully supported before, but it was an easy fix. I added some more descriptive error messages and also updated the documentation here. The error is that if you have a rest_pandas renderer in DEFAULT_RENDERER_CLASSES, you need to import rest_pandas before importing anything from rest_framework.views or you may end up with a recursive import.

One way to fix the import order is to add rest_pandas to your INSTALLED_APPS, though you'll also want to import PandasMixin to add to your own views.

@gatensj
Copy link

gatensj commented Sep 27, 2017

Hey there. Thanks for the response. I've followed your instructions listed above, and am still running into issues. In my settings file, I've included rest_pandas in my included apps. Then in my models, I defined a objects = DataFrameManager(), right before the class Meta definition. My serializer is a ModelSerializer. And my View is passing PandasMixin, ListAPIView. Listed below is my code.

`

(serializer.py)

    class FfAllFactorsDailyPandaSerializer(ModelSerializer):
            class Meta:
                model = FfAllFactorsDailyModel
                fields = (
                    'date',
                    'mktrf',
                    'smb',
                    'hml',
                    'rf',
                    'umd',
                )

(views.py)

    class FfAllSeriesView(PandasMixin, ListAPIView):
            queryset = FfAllFactorsDailyModel.objects.all()
            serializer_class = FfAllFactorsDailyPandaSerializer
            permission_classes = (FfAllPermission,)

            def filter_queryset(self, qs):
                # At this point, you can filter queryset based on self.request or other
                # settings (useful for limiting memory usage).  This function can be
                # omitted if you are using a filter backend or do not need filtering.
                return qs

`

Much to my chagrin, I'm still getting a "Response data is a <class 'rest_framework.utils.serializer_helpers.ReturnList'>, not a DataFrame!" error. In short, what am I doing wrong? I thought the (objects = DataFrameManager()) defined in the model, would return a DataFrame. Thanks in advance for the assist. Take care and have a good day.

Sincerely,

JG

@FlipperPA
Copy link
Author

FlipperPA commented Oct 2, 2017

Thanks very much for our efforts, @sheppard - but I'm having a similar issue to JG, and I think I'm close. I've installed the latest rest_pandas from master. I've added rest_pandas to my INSTALLED_APPS. Here's are my settings:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_METADATA_CLASS': 'home.adapters.TypesFiltersMetadata',
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10,

    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
        'rest_pandas.renderers.PandasExcelRenderer',
    ),
}

I've modified my ViewSets to look like this:

class MyExampleViewSet(PandasMixin, ReadOnlyModelViewSet):
    queryset = MyExampleModel.objects.all()
    serializer_class = MyExampleSerializer
    permission_classes = (MyPermission,)
    filter_backends = (DjangoFilterBackend,)
    filter_fields = (
        'date',
    )

I can browse the HTML version of the DRF API, and xlsx appears as an option in the drop down alongside json and html. However, when I select xlsx I'm getting this error, despite having extended PandasMixin:

Response data is a OrderedDict, not a DataFrame! Did you extend PandasMixin?

I've started digging through the code, but any help you could provide would be fantastic. Thanks again for your efforts on DRP - much appreciated!

@mehta
Copy link

mehta commented Apr 4, 2018

Remove DEFAULT_PAGINATION_CLASS in your settings.

@sssolid
Copy link

sssolid commented May 30, 2018

Response data is a OrderedDict, not a DataFrame! Did you extend PandasMixin?

In my case, the above error was directly related to pagination and I didn't want to disable DEFAULT_PAGINATION_CLASS. As a quick workaround for the problem, I override ListMixin and check to see if the URL parameter "format" exists. I don't use a format extension (/path.csv) so I didn't bother to add it.

from django.conf import settings
from rest_framework import mixins

class PaginatedPandaListMixin(mixins.ListModelMixin):

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        # Check if format parameter overridden
        try:
            format_param = settings.URL_FORMAT_OVERRIDE
        except AttributeError:
            format_param = 'format'
        # Don't paginate if response is for DRP or API
        if request.GET.get(format_param) and not request.GET.get(format_param) == 'api':
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

I then use the above mixin on my viewsets and all is well.

class CustomViewSet(PandasMixin, PaginatedPandaListMixin, ModelViewSet):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants