¡Esta es una revisión vieja del documento!
Official page for Django REST Framework is: http://www.django-rest-framework.org/
pip install djangorestframework
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 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.
serializer = CommentSerializer(comment, many=False) serializer.data # {'email': u'leila@example.com', 'content': u'foo bar'....
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 # <Comment object at 0x10633b2d0>
serializer = CommentSerializer(comment, data=data) # Update `comment`
Or limited fields:
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
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_<field> method.
queryset = Book.objects.all() serializer = BookSerializer(queryset, many=True) serializer.data
data = [ {'title': 'The bell jar', 'author': 'Sylvia Plath'}, {'title': 'For whom the bell tolls', 'author': 'Ernest Hemingway'} ] serializer = BookSerializer(data=data, many=True)
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()
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
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.']}
Adding .validate_<fieldname> 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
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
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()
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)
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.
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)
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')
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
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. paginate_bypaginate_by_parampagination_serializer_classpage_kwargfilter_backendsget_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
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
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 create custom mixins.
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.
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.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 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:
JSONParserYAMLParserXMLParserFormParserMultiPartParserFileUploadParser
You can code your own parse inheriting from BaseParser. There also are other third party packages with their own parsers MessagePack, CamelCaseJSON…
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.
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.
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.
This authentication scheme uses HTTP Basic Authentication, signed against a user's username and password, and is generally only appropriate for testing.
Session authentication is appropriate for AJAX clients that are running in the same session context as your website.
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 line tool may be useful 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' }
… like OAuth or Hawk: http://www.django-rest-framework.org/api-guide/authentication#api-reference.
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 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
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)
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.
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.
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)
AnonRateThrottleUserRateThrottleScopedRateThrottle
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
The APIRequestFactory class supports the same methods as 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 force the user authentication if you want authomatically athenticate the request.
All the mentioned classes are in the rest_framework.test package.
There are some classes to perform the tests:
APISimpleTestCaseAPITransactionTestCaseAPITestCaseAPILiveServerTestCasefrom 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}')
APIClient to make calls to the API. It has the same methods as APIRequestFactory (get(), post()…) and adds others like .login(), .credentials()…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:
get_queryset() method instead queryset attribute:class InvitationsView(ListCreateAPIView): serializer_class = InvitationRequestSerializer def get_queryset(self): return InvitationRequest.objects
urlpatterns = patterns('', url(r'^invitations/$', InvitationsView.as_view(), name='invitations'), )