Django REST Framework - transitioning model to ViewSet via router

I'm trying to create a REST-ModelViewSet that doesn't have a predefined model, but takes the model when registering with the router. I need this to dynamically add models to the REST API without setting up new views or serializers.

My idea was to pass the model in kwargs

from __init__

, but I can't figure out how to do it correctly. Here's what I've tried:

//Viewset
class ThemeViewSet(viewsets.ModelViewSet):
    def __init__(self, **kwargs):
        self.model = kwargs['model']
        self.serializer_class = None
        super(ThemeViewSet, self).__init__(**kwargs)

    def get_serializer_class(self):
        if self.serializer_class is not None:
            return self.serializer_class

        class ThemeSerializer(serializers.HyperlinkedModelSerializer):
            class Meta:
                model = self.model

        self.serializer_class = ThemeSerializer
        return self.serializer_class

//Router:
router = routers.DefaultRouter()
router.register(r'mytheme', ThemeViewSet(model=mytheme), base_name='mytheme')

      

Now if I try print self.model

in __init__

, it shows correctly <class 'myapp.models.mytheme'>

in the console, but Django still returns an error:

AttributeError at /api/mytheme/
This method is available only on the view class.

      

This error is classonlymethod

thrown with -decorator. I really don't know what to do with this, is there a way to pass the model to __init__

, or is there another approach I can try?

(I know wq.db.rest has a router that does what I want, but I don't want to use wq. I haven't tried tastypie, would that make it easier / possible?)

Thanks in advance!

+1


source to share


1 answer


Django REST Framework expects a ViewSet

class to be
passed to a router, not a view instance. This is because an instance must be created for every request, which prevents a lot of ugly shared state issues and also follows the standard Django class based views.

You might be in luck if you have a method that creates a custom class ViewSet

based on the model passed to it:

class ThemeViewSet(viewsets.ModelViewSet):

    @classmethod
    def create_custom(cls, **kwargs):
        class CustomViewSet(cls):
            model = kwargs["model"]
            queryset = kwargs["model"].objects.all()

        return CustomViewSet

      

Note that I am also setting queryset

for submission and DRF is no longer only accepting model

since 2.4 was released.



This will create a new class on every call and the model will automatically be set to the model that is passed into it. When registering it with a router, you would do something like:

router.register(r'mytheme', ThemeViewSet.create_custom(model=mytheme), base_name='mytheme')

      

This way you will still be passing the class ViewSet

to the router, but it will be configured for the model being passed. You must make sure to install base_name

, or the router will not be able to generate view names and you will end up with errors.

+3


source







All Articles