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!
source to share
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.
source to share