====== Django REST Framework ====== ===== First Steps ===== Official page for Django REST Framework is: http://www.django-rest-framework.org/ ==== Installation ==== pip install djangorestframework ===== Routers ===== To define the URL for the API REST you can use router classes, which mount a list of relationships between url and views. It can be used in the ''urlpatterns'' settings variable. They have a ''register( prefix, viewset)'', prefix is used to indicate URL patterns and viewset the concrete view. from rest_framework import routers router = routers.SimpleRouter() router.register(r'users', UserViewSet) router.register(r'accounts', AccountViewSet) urlpatterns = router.urls There are two types of predefined routers: * ''SimpleRouter'' (you can define if the last slash should be taken into account with the parametere ''trailing_slash''). * ''DefaultRouter'', which allow to specify the format. They are in the package ''rest_framework.routers''. However, you could also add your views in this way: urlpatterns = patterns('', url(r'^invitations/$', 'core.invitations.views.invitations'), ) ===== Serializers ===== Serializers allow complex datatypes to be converted to native Python objects which can then be easily rendered into JSON, XML, or other formats. \\ Having an object like: class Comment(object): def __init__(self, email, content, created=None): self.email = email self.content = content self.created = created or datetime.datetime.now() comment = Comment(email='leila@example.com', content='foo bar') We can declare a serializer like this: from rest_framework import serializers class CommentSerializer(serializers.Serializer): email = serializers.EmailField() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() def restore_object(self, attrs, instance=None): if instance is not None: instance.email = attrs.get('email', instance.email) instance.content = attrs.get('content', instance.content) instance.created = attrs.get('created', instance.created) return instance return Comment(**attrs) It's format by fields which will be serialized/deserialized. The ''restore_object'' method allows the deserializing of complex data. I we didn't define this method, the deserializing would return a dictionary. ==== Serializing\Deserializing ==== === Serializing === serializer = CommentSerializer(comment, many=False) serializer.data # {'email': u'leila@example.com', 'content': u'foo bar'.... === Deserializing === Having a input text stream from a json, StringIO allows that from a string: from StringIO import StringIO from rest_framework.parsers import JSONParser stream = StringIO(json) data = JSONParser().parse(stream) # it's a dict (?) serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.object # === Updating a serialized object === serializer = CommentSerializer(comment, data=data) # Update `comment` Or limited fields: serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True) === Changing representation === For example, we have a description and a descriptionhtml fields. When serialize description we want that descriptionhtml is shown as markup. We can do it like this: description = serializers.TextField() descriptionhtml = serializers.TextField(source='description', read_only=True) def transform_descriptionhtml(self, obj, value): from django.contrib.markup.templatetags.markup import markdown return markdown(value) It uses the ''transform_'' method. === Serializing multiple objects === queryset = Book.objects.all() serializer = BookSerializer(queryset, many=True) serializer.data === Deserializing multiple objects === data = [ {'title': 'The bell jar', 'author': 'Sylvia Plath'}, {'title': 'For whom the bell tolls', 'author': 'Ernest Hemingway'} ] serializer = BookSerializer(data=data, many=True) === Deserializing multiple objects for update === It changes the title for 3 and 4 books: queryset = Book.objects.all() data = [ {'id': 3, 'title': 'The Bell Jar'}, {'id': 4, 'title': 'For Whom the Bell Tolls'} ] serializer = BookSerializer(queryset, data=data, many=True) serializer.is_valid() serializer.save() You may want to allow new items to be created, and missing items to be deleted. To do so, pass ''allow_add_remove=True'' to the serializer. serializer = BookSerializer(queryset, data=data, many=True, allow_add_remove=True) serializer.is_valid() serializer.save() === Changing the way to identify an object === To do it (required when perform a bulk edit) we must override the ''get_identity'' to determine the identifier. If not, it will use ''id'' field. class AccountSerializer(serializers.Serializer): slug = serializers.CharField(max_length=100) created = serializers.DateTimeField() def get_identity(self, data): try: return data.get('slug', None) except AttributeError: return None ==== Validation ==== When deserializing data you always need to call ''is_valid()'' before access the object. It will test if the deserialization was ok, if not the ''.errors'' will contain which problems were found. serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'}) serializer.is_valid() # False serializer.errors # {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']} === Specify a field validation === Adding ''.validate_'' method to Serializer subclasses you can concrete a validation. It takes a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument. It should either just return the attrs dictionary or raise a ValidationError. def validate_title(self, attrs, source): value = attrs[source] if "django" not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return attrs === Specify a object validation === Adding a method called ''.validate()'' to Serializer subclasses you can concrete an object validation. This method takes a single argument, which is the attrs dictionary. It should raise a ValidationError if necessary, or just return attrs: def validate(self, attrs): if attrs['start_date'] > attrs['finish_date']: raise serializers.ValidationError("finish must occur after start") return attrs ==== Saving an object ==== To save the deserialized objects created by a serializer, call the ''.save()'' method. You can override the default save behaviour by overriding the .save_object(obj) method on the serializer class. if serializer.is_valid(): serializer.save() ==== Nested objects ==== We can use a Serializer class as a field of another class: class UserSerializer(serializers.Serializer): email = serializers.EmailField() username = serializers.CharField(max_length=100) class CommentSerializer(serializers.Serializer): user = UserSerializer() content = serializers.CharField(max_length=200) created = serializers.DateTimeField() If the object is optional (accept None as value), we will use the ''required=False'' flag: user = UserSerializer(required=False) # May be an anonymous user. If the object should be a list of items, we will use ''many=True'' flag: edits = EditItemSerializer(many=True) ==== Model classes ==== You can work with model classes using a subclass ''Meta'' which field model should be assigned with its corresponding model class: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account You can specify which fields you will use: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'account_name', 'users', 'created') In relationships you can specify which level of nested objects it will include: class AccountSerializer(serializers.ModelSerializer): class Meta: model = Account fields = ('id', 'account_name', 'users', 'created') depth = 1 Or include read only fields with ''read_only_fields'' field, or write only fields with ''write_only_fields'' field. Even you can add more fields to your serializer class than those that have the model class. ==== Notes ==== * Here you can find which fields you could use or how to create your own field type: [[http://www.django-rest-framework.org/api-guide/fields]] * If you work with relational data bases, here you'll find how to serialize relations: [[http://www.django-rest-framework.org/api-guide/relations]] ===== Views ===== ==== Class Based Views ==== REST framework provides an ''APIView'' class to inherit from. It works... Giving a ''Request'' instance to handler methods. Handler methods may return a ''Response'' object. ''APIException'' exceptions are caught properly by the ''APIView'' class. Requests are authenticated before dispatching it to the handler method. Handler methods are those like ''.get()'', ''put()'', ''patch()'', ''delete()'', or ''.post()''. The number of attributes may be set on the class. ''.renderer_classes'', ''.parser_classes'', ''.authentication_classes'', ''.throttle_classes'', ''.permission_classes'', and ''.content_negotiation_class'' attributes can be set into the ''APIView'' subclass. There is a decorator which you can use it to indicate that a function will work as handler, it receives which handlers will accept as string list ''@api_vide(['GET', 'POST'])''. There's also another decorator to use to ensure that concrete restrictions are applied: ''@throttle_classes'' Other usefull methods are also provided (''.get_renderers(self)'', ''.get_parsers(self)'', ''.get_authenticators(self)'', ''.get_throttles(self)'', ''.get_permissions(self)'', ''.get_content_negotiator(self)'') Methods that are called before dispatching to the handler method are ''.check_permissions(self, request)'', ''.check_throttles(self, request)'', ''.perform_content_negotiation(self, request, force=False)''. There are other useful methods to override which are called before or after calling handler methods: * ''.initial(self, request, *args, **kwargs)'' * ''.handle_exception(self, exc)'', any exception thrown by the handler method will be passed to this method. * ''.initialize_request(self, request, *args, **kwargs)'' * ''.finalize_response(self, request, response, *args, **kwargs)'' from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import authentication, permissions class ListUsers(APIView): authentication_classes = (authentication.TokenAuthentication,) permission_classes = (permissions.IsAdminUser,) def get(self, request, format=None): usernames = [user.username for user in User.objects.all()] return Response(usernames) ==== Generic views ==== They are pre-built views that allow to compose reusable behaviour. To use them in URLconf you would include: url(r'^/users/', ListCreateAPIView.as_view(model=User), name='user-list') === Examples === from django.contrib.auth.models import User from myapp.serializers import UserSerializer from rest_framework import generics from rest_framework.permissions import IsAdminUser class UserList(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAdminUser,) paginate_by = 100 # ... or... class UserList(generics.ListCreateAPIView): queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAdminUser,) def get_paginate_by(self): # Use smaller pagination for HTML representations. if self.request.accepted_renderer.format == 'html': return 20 return 100 === Basic attributes === * ''queryset'', all the elements from this view. You must either set this attribute, or override the ''get_queryset()'' method. * ''serializer_class'', the serializer class that should be used for validating and deserializing input, and for serializing output. You must either set this attribute, or override the get_serializer_class() method. * ''lookup_field'', the model field that should be used to for performing object lookup of individual model instances. Defaults to ''pk''. * ''lookup_url_kwarg'', the URL keyword argument that should be used for object lookup. * ''model'', this shortcut may be used instead of setting either (or both) of the queryset/serializer_class attributes, although using the explicit style is generally preferred. If used instead of serializer_class, then then ''DEFAULT_MODEL_SERIALIZER_CLASS'' setting will determine the base serializer class. === Pagination & filtering=== * ''paginate_by'' * ''paginate_by_param'' * ''pagination_serializer_class'' * ''page_kwarg'' * ''filter_backends'' === Base methods === * ''get_queryset(self)'' returns all the elements from the type that use this view. def get_queryset(self): user = self.request.user return user.accounts.all() * ''get_object(self)'' an object instance that should be used for details view (using the ''lookup_field'' to filter: def get_object(self): queryset = self.get_queryset() filter = {} for field in self.multiple_lookup_fields: filter[field] = self.kwargs[field] obj = get_object_or_404(queryset, **filter) self.check_object_permissions(self.request, obj) return obj * get_filter_backends(self) * get_serializer_class(self) * get_paginate_by(self) === Save and deltion hooks === * ''pre_save(self, obj) '' * ''post_save(self, obj, created=False)'' * ''pre_delete(self, obj)'' * ''post_delete(self, obj) '' def pre_save(self, obj): obj.owner = self.request.user ==== Mixins ==== They are classes which provide the basic actions rather than define ''get()'' and ''post()'' methods. * ''ListModelMixin'', to list a queryset. * ''CreateModelMixin'', to create a new model instance. * ''RetrieveModelMixin'', to return a model instance. * ''UpdateModelMixin'', tu update a model instance. * ''DestroyModelMixin'', to destroy. You can [[http://www.django-rest-framework.org/api-guide/generic-views#creating-custom-mixins|create custom mixins]]. ==== Concrete views ==== When using generic views you can use this preconf APIView's: | | post | get (single) | get (collection) | put | patch | delete | | CreateAPIView | x | | | | | | | ListAPIView | | | x | | | | | RetrieveAPIView | | x | | | | | | DestroyAPIView | | | | | | x | | UpdateAPIView | | | | x | x | | | ListCreateAPIView | x | | x | | | | | RetrieveUpdateAPIView | | x | | x | x | | | RetrieveDestroyAPIView | | x | | | | x | | RetrieveUpdateDestroyAPIView | | x | | x | x | x | They are in the package ''rest_framework.generics''. ===== How to... ===== ==== Request objects are formed? ==== * ''request.DATA'' returns the parsed content of the request body. * ''request.FILES'' returns any uploaded files that may be present in the content of the request body. * ''request.QUERY_PARAMS'' is a more correctly named synonym for request.GET. * ''request.user'' typically returns an instance of ''django.contrib.auth.models.User''. * ''request.auth'' returns any additional authentication context. * ''request.method'' returns the uppercased string representation of the request's HTTP method. * ''request.content_type'', returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided. * ''request.stream'' returns a stream representing the content of the request body. ==== Response objects are created? ==== The renderers used by the Response class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the Response object. * ''data'': The serialized data for the response. * ''status'': A status code for the response. Defaults to 200. See also status codes. * ''template_name'': A template name to use if HTMLRenderer is selected. * ''headers'': A dictionary of HTTP headers to use in the response. * ''content_type'': The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly. ==== Parsers work? ==== Parsers are classes to convert from a datatype to another. We can set default parsers for all our API REST or any of the views. There are some default parsers: * ''JSONParser'' * ''YAMLParser'' * ''XMLParser'' * ''FormParser'' * ''MultiPartParser'' * ''FileUploadParser'' You can code your own parse inheriting from ''BaseParser''. There also are other third party packages with their own parsers ''MessagePack'', ''CamelCaseJSON''... ==== Filtering works? ==== To restrict the returned queryset you can filter... === Against the current user === class PurchaseList(generics.ListAPIView) serializer_class = PurchaseSerializer def get_queryset(self): user = self.request.user return Purchase.objects.filter(purchaser=user) === Against the url === url('^purchases/(?P.+)/$', PurchaseList.as_view()), ... class PurchaseList(generics.ListAPIView) serializer_class = PurchaseSerializer def get_queryset(self): username = self.kwargs['username'] return Purchase.objects.filter(purchaser__username=username) === Against query parameters === class PurchaseList(generics.ListAPIView) serializer_class = PurchaseSerializer def get_queryset(self): queryset = Purchase.objects.all() username = self.request.QUERY_PARAMS.get('username', None) if username is not None: queryset = queryset.filter(purchaser__username=username) return queryset === Others === * [[http://www.django-rest-framework.org/api-guide/filtering]] Django REST Framework provides several ways to return a queryset only defining order or fields. Also you can define your own filter classes. ==== Pagination works? ==== ==== Others ==== === Format suffixes === It's how to return the Response with a concrete format indicated by the url: [[http://www.django-rest-framework.org/api-guide/format-suffixes]]. === Return URLs === * [[http://www.django-rest-framework.org/api-guide/reverse]] When returning URLs to other resources it's better to return the absolute url rather than the relative. Even if it was an identifier; if the string represents a resource, in REST it must be the resource absolute path. To do so in Django REST Framework you could use ''reverse'' and ''reverse_lazy'' functions. === Configure === * [[http://www.django-rest-framework.org/api-guide/settings]] You can configure features like default classes (renderers, parsers, authentications...), generic view settings (pagination, ordering...), el formato de las fechas... ===== Authentication and permissions ===== ==== Authentication ==== ''request.user'' will be set to an instance of ''User'' class. ''request.auth'' is set with addictional authentication information like authentication token. If the user is not authenticated the ''request.user'' is set as ''AnonymousUser'' instance and ''request.auth'' as None. This behaviour can be modified changing ''UNAUTHENTICATED_USER'' and ''UNAUTHENTICATED_TOKEN'' settings. \\ You can set the authentication classes (authentication schemes) globally in ''DEFAULT_AUTHENTICATION'' setting, for example: REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) } ... Or in the ''APIView'': class ExampleView(APIView): authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated,) def get(self, request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. 'auth': unicode(request.auth), # None } return Response(content) @api_view(['GET']) @authentication_classes((SessionAuthentication, BasicAuthentication)) @permission_classes((IsAuthenticated,)) def example_view(request, format=None): content = { 'user': unicode(request.user), # `django.contrib.auth.User` instance. 'auth': unicode(request.auth), # None } return Response(content) There are two responses to non authentication requests: ''HTTP 401 Unauthorized'' and ''HTTP 403 Permission Denied''. The kind of response that will be used depends on the authentication scheme. ==== Authentication modes ==== === Basic Authentication === This authentication scheme uses HTTP Basic Authentication, signed against a user's username and password, and is generally only appropriate for testing. === Session Authentication === Session authentication is appropriate for AJAX clients that are running in the same session context as your website. === Token Authentication === To use the TokenAuthentication scheme, include ''rest_framework.authtoken'' in your ''INSTALLED_APPS'' setting: INSTALLED_APPS = ( ... 'rest_framework.authtoken' ) You'll also need to create tokens for your users. from rest_framework.authtoken.models import Token token = Token.objects.create(user=...) print token.key For clients to authenticate, the token key should be included in the Authorization HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example: Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b The curl command for obtain the token: curl -X POST http://127.0.0.1:8000/api-token-auth/ -H "Content-type: application/json" -d '{"username": "alfred", "password": "test"}' The curl command for testing token authenticated APIs. For example: curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' If successfully authenticated, ''TokenAuthentication'' provides the ''request.auth'' as a ''rest_framework.authtoken.models.BasicToken'' instance. To create a token for all the users that already exist in your DB you'll do: for user in User.objects.all(): Token.objects.get_or_create(user=user) If you want every user to have an automatically generated Token, you can simply catch the User's post_save signal (it should be in ''models.py'' or in any code imported by Django startup): @receiver(post_save, sender=get_user_model()) def create_auth_token(sender, instance=None, created=False, **kwargs): if created: Token.objects.create(user=instance) You will need to provide a token after given the username and password. It's already developed using the ''obtain_auth_token'' view: urlpatterns += patterns('', url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token') ) It will return a JSON response when valid username and password fields are POSTed to this view. Note that this does not use the default renderer and parser classes. If you needed another version of this behaviour you should override the ''ObtainAuthToken'' view class, and using it in your url conf instead. { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } === Others === ... like OAuth or Hawk: [[http://www.django-rest-framework.org/api-guide/authentication#api-reference]]. === Custom Authentication === To implement a custom authentication scheme, subclass BaseAuthentication and override the .authenticate(self, request) method. The method should return a two-tuple of (user, auth) if authentication succeeds, or None otherwise. You also may want to raise an ''AuthenticationFailed'' exception from the ''.authenticate()'' method. class ExampleAuthentication(authentication.BaseAuthentication): def authenticate(self, request): username = request.META.get('X_USERNAME') if not username: return None try: user = User.objects.get(username=username) except User.DoesNotExist: raise exceptions.AuthenticationFailed('No such user') return (user, None) ==== Permissions ==== Permissions are defined as a list of permission classes. When a view is called a permission list classes is checked, if any of them fails an ''exceptions.PermissionDenied'' exception will be raised. \\ To check permissions in an Object level ''.get_object()'' will be called in the view. Then you'll need to explicitly call the ''.check_object_permissions(request, obj)'' method on the view at the point at which you've retrieved the object. This will either raise a ''PermissionDenied'' or ''NotAuthenticated'' exception, or simply return if the view has the appropriate permissions. def get_object(self): obj = get_object_or_404(self.get_queryset()) self.check_object_permissions(self.request, obj) return obj === Setting the permisson policy === It may be set globally, using the DEFAULT_PERMISSION_CLASSES setting. REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) } If not specified, this setting defaults to allowing unrestricted access: 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', ) You can also set the authentication policy on a per-view, or per-viewset basis, using the APIView class based views: class ExampleView(APIView): permission_classes = (IsAuthenticated,) def get(self, request, format=None): content = { 'status': 'request was permitted' } return Response(content) Or, if you're using the @api_view decorator with function based views. @api_view('GET') @permission_classes((IsAuthenticated, )) def example_view(request, format=None): content = { 'status': 'request was permitted' } return Response(content) === Permission classes === * ''AllowAny'', will allow unrestricted access, regardless of if the request was authenticated or unauthenticated. * ''IsAuthenticated'', will deny permission to any unauthenticated user, and allow permission otherwise. * ''IsAdminUser'', will deny permission to any user, unless ''user.is_staff'' is True. * There are more in: [[http://www.django-rest-framework.org/api-guide/permissions#api-reference]] To implement a custom permission class, override ''BasePermission'' and implement either, or both, of the methods: ''.has_permission(self, request, view)'', ''.has_object_permission(self, request, view, obj)''. The methods should return True if the request should be granted access, and False otherwise. ==== Throttling ==== They also authorises requests. However they indicate a temporary state, and are used to control the rate of requests that clients can make. Before running the main body of the view each throttle in the list is checked. If any throttle check fails an exceptions.Throttled exception will be raised. \\ The default throttling policy may be set globally, using the ''DEFAULT_THROTTLE_CLASSES'' and ''DEFAULT_THROTTLE_RATES'' settings: REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' } } The rate descriptions used in ''DEFAULT_THROTTLE_RATES'' may include second, minute, hour or day as the throttle period. \\ You can also set the throttling policy on a per-view or per-viewset basis, using the APIView class based views. class ExampleView(APIView): throttle_classes = (UserRateThrottle,) def get(self, request, format=None): content = { 'status': 'request was permitted' } return Response(content) @api_view('GET') @throttle_classes([UserRateThrottle]) def example_view(request, format=None): content = { 'status': 'request was permitted' } return Response(content) === Some throttles === * ''AnonRateThrottle'' * ''UserRateThrottle'' * ''ScopedRateThrottle'' === Custom throttles === To create a custom throttle, override BaseThrottle and implement ''.allow_request(self, request, view)''. The method should return ''True'' if the request should be allowed, and ''False'' otherwise. \\ Optionally you may also override the .wait() method. If implemented, ''.wait()'' should return a recommended number of seconds to wait before attempting the next request, or ''None''. The ''.wait()'' method will only be called if ''.allow_request()'' has previously returned ''False''. \\ The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests. class RandomRateThrottle(throttles.BaseThrottle): def allow_request(self, request, view): return random.randint(1, 10) == 1 ===== Testing ===== The ''APIRequestFactory'' class supports the same methods as [[https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.client.RequestFactory|Django RequestFactory class]]. So methods like ''.get()'', ''.post()'', ''.put()'', ''.patch()'', ''.delete()'', ''.head()'' and ''.options()'' are available to use like that... from rest_framework.test import APIRequestFactory factory = APIRequestFactory() request = factory.post('/notes/', {'title': 'new idea'}, format='json') In methods like ''post'', ''put'' and ''patch'' you can specify the request format using the ''format'' parameter. Also you could specify the content-type and send raw data: request = factory.post('/notes/', json.dumps({'title': 'new idea'}), content_type='application/json') You can [[http://www.django-rest-framework.org/api-guide/testing#forcing-authentication|force the user authentication]] if you want authomatically athenticate the request. All the mentioned classes are in the ''rest_framework.test'' package. ==== Test cases ==== There are some classes to perform the tests: * ''APISimpleTestCase'' * ''APITransactionTestCase'' * ''APITestCase'' * ''APILiveServerTestCase'' from django.core.urlresolvers import reverse from rest_framework import status from rest_framework.test import APITestCase class AccountTests(APITestCase): def test_create_account(self): url = reverse('account-list') data = {'name': 'DabApps'} response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.data, data) You can test using the ''response.data'' which is preferred over parsing the ''response.content'': # Better this... response = self.client.get('/users/4/') self.assertEqual(response.data, {'id': 4, 'username': 'lauren'}) # ... than this: response = self.client.get('/users/4/') self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'}) If you are testing views, in order to access ''response.content'', you'll first need to render the response. view = UserDetail.as_view() request = factory.get('/users/4') response = view(request, pk='4') response.render() # Cannot access `response.content` without this. self.assertEqual(response.content, '{"username": "lauren", "id": 4}') ==== Notes ==== * You could use the ''APIClient'' to make calls to the API. It has the same methods as ''APIRequestFactory'' (''get()'', ''post()''...) and adds others like ''.login()'', ''.credentials()''... ===== Notes ===== ==== On using MongoEngine ==== As MongoEngine documents are not Django model classes so they are not compatible inside the Django REST Framework. However you can use it if you define some things: * You must define your serializer class. Here you should mark which properties are requiered. * Views should use ''get_queryset()'' method instead ''queryset'' attribute: class InvitationsView(ListCreateAPIView): serializer_class = InvitationRequestSerializer def get_queryset(self): return InvitationRequest.objects * The urlpatterns variable should be defined as follows (not with a router): urlpatterns = patterns('', url(r'^invitations/$', InvitationsView.as_view(), name='invitations'), )