30

In the DRF source code, there's a get_serializer method. It wasn't inherited from object and it's not a method in the CreateModelMixin class. Where does this method come from?

serializer = self.get_serializer(data=request.data)

Here's the larger chunk of code for context.

from __future__ import unicode_literals

from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings


class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': data[api_settings.URL_FIELD_NAME]}
        except (TypeError, KeyError):
            return {}

There are a few SO posts that that also use this method. Like this, this, and this. But I still can't figure out where the implementation is.

Community
  • 1
  • 1
k-mad
  • 423
  • 1
  • 4
  • 8

3 Answers3

24

CreateModelMixin along with all other mixin classes (Eg. ListModelMixin, UpdateModelMixin etc) are defined in rest_framework/mixins.py file.

These mixin classes provide all the basic CRUD operations on a model. You just need to define a serializer_class and queryset in your generic view to perform all these operations. DRF has separated out these common functionality in separate mixin classes so that they can be injected/mixed-in in a view and used as and when required.

In the DRF source code, there's a get_serializer method. It wasn't inherited from object and it's not a method in the CreateModelMixin class. Where does this method come from?

In the GenericAPIView, get_serializer method is defined. The combination of different mixin classes along with GenericAPIView class provide us different generic views for different use cases.

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)

Other generic views then inherit the relevant mixin along with GenericAPIView.

Eg. CreateAPIView inherit the CreateModelMixin along with GenericAPIView to provide create-only endpoints.

# rest_framework/generics.py
class CreateAPIView(mixins.CreateModelMixin,
                    GenericAPIView):
    ...
Rahul Gupta
  • 46,769
  • 10
  • 112
  • 126
  • 2
    I think I'm getting closer to understanding. I looked around in the `GenericAPIView` code and it has the implementation for `get_serializer`. To use the `CreateModelMixin` I need to also use `GenericAPIView`. `class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):` works. `class CreateAPIView(mixins.CreateModelMixin):` doesn't work because `CreateModelMixin` depends on `GenericAPIView` to implement the `get_serializer` method. This must be what you mean by __Other generic views then inherit the relevant mixin along with GenericAPIView.__ – k-mad Jul 04 '16 at 21:47
  • 1
    @k-mad Yes, it should be `get_serializer` method instead, i have corrected it. `get_serializer()` gets the relevant serializer class using `get_serializer_class` method. Then it creates the instance of that class inside that method. – Rahul Gupta Jul 05 '16 at 05:11
  • 1
    Yes, I think you should read more about mixins in general. This should help in understanding the real purpose of having mixins for all these operations. Mixins help in basically injecting code to a class. For starting up, you can maybe check this [wiki link](https://en.wikipedia.org/wiki/Mixin) – Rahul Gupta Jul 05 '16 at 05:15
4

It helps if you understand class inheritance (not suggesting you don't, though).

CreateModelMixin will be used by a class based view, for example:

class YourViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):

get_serializer is a method available through GenericViewSet (although there are probably other View classes that it's available on, too).

So, basically if you would use CreateModelMixin only get_serializer would not be available. (You probably wouldn't do that anyway). It's only because you inherit from another class besides CreateModelMixin that the get_serializer method is available at the moment that the create method is executed.

I could give a simple example of what's happening, while I am at it. It's just to give a more simple overview of what's happening.

disclaimer: you might want to keep in mind that I am a junior developer, so this may not be the most Pythonic code out there :P.

class MyMixin:
    def create(self):
        serializer = self.get_serializer()
        return serializer


class FakeView:
    def get_serializer(self):
        return "Sadly I am just a string, but I could've been a serializer."


class MyFakeView(MyMixin, FakeView):
    pass

view = MyFakeView()

serializer = view.create()

print(serializer)

executing this would show you:

Sadly I am just a string, but I could've been a serializer.

Where if you would define MyFakeView like below,

class MyFakeView(MyMixin):
    pass

you would receive the following error:

AttributeError: 'MyFakeView' object has no attribute 'get_serializer'

Rik Schoonbeek
  • 3,908
  • 2
  • 25
  • 43
2

You can see the __file__ or __module__ member of the method (if it has them) to learn that. inspect also has getsourcefile and getsourcelines that use data from the function's code object, specifically, <function>.f_code.co_filename and .co_firstline.

For example, this retrieves source information for a method inherited from DictMixin:

>>> c=ConfigParser._Chainmap()
>>> f=c.__len__
>>> dump(f.__code__)    # my custom function that dumps attributes,
                        # see https://github.com/native-api/dump
<...>
co_filename : /usr/lib/python2.7/UserDict.py
co_firstlineno : 179
<...>

# same with inspect
>>> inspect.getsourcefile(f)
'/usr/lib/python2.7/UserDict.py'
>>> inspect.getsourcelines(f)
(['    def __len__(self):\n', '        return len(self.keys())\n'], 179)
>>> inspect.getsource(f)
'    def __len__(self):\n        return len(self.keys())\n'

# same with __file__
>>> sys.modules[f.__module__].__file__
'/usr/lib/python2.7/UserDict.pyc'
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
  • Maybe I don't understand what you're saying but I don't know how to inspect a method if I can't find it. When I use inspect on the `CreateModelMixin` class, I get the source code that I posted. If I call the create method an instance of `CreateModelMixin`, I get `AttributeError: 'CreateModelMixin' object has no attribute 'get_serializer'` – k-mad Jul 04 '16 at 20:58