From dd14c6c88ba210bac8349041b8db486576539249 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Mon, 3 Nov 2014 11:21:29 +0000 Subject: Latest docs release --- api-guide/authentication.html | 15 +- api-guide/format-suffixes.html | 11 +- api-guide/metadata.html | 324 ++++++++++++++++++++++++++++++++++ api-guide/validators.html | 388 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 728 insertions(+), 10 deletions(-) create mode 100644 api-guide/metadata.html create mode 100644 api-guide/validators.html (limited to 'api-guide') diff --git a/api-guide/authentication.html b/api-guide/authentication.html index 3118ff34..420a1874 100644 --- a/api-guide/authentication.html +++ b/api-guide/authentication.html @@ -206,6 +206,7 @@ a.fusion-poweredby {
  • JSON Web Token Authentication
  • Hawk HTTP Authentication
  • HTTP Signature Authentication
  • +
  • Djoser
  • @@ -337,12 +338,13 @@ print token.key

    Generating Tokens

    If you want every user to have an automatically generated Token, you can simply catch the User's post_save signal.

    -
    from django.contrib.auth import get_user_model
    +
    from django.conf import settings
    +from django.contrib.auth import get_user_model
     from django.db.models.signals import post_save
     from django.dispatch import receiver
     from rest_framework.authtoken.models import Token
     
    -@receiver(post_save, sender=get_user_model())
    +@receiver(post_save, sender=settings.AUTH_USER_MODEL)
     def create_auth_token(sender, instance=None, created=False, **kwargs):
         if created:
             Token.objects.create(user=instance)
    @@ -356,9 +358,10 @@ for user in User.objects.all():
         Token.objects.get_or_create(user=user)
     

    When using TokenAuthentication, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the obtain_auth_token view to your URLconf:

    -
    urlpatterns += patterns('',
    -    url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token')
    -)
    +
    from rest_framework.authtoken import views
    +urlpatterns += [
    +    url(r'^api-token-auth/', views.obtain_auth_token)
    +]
     

    Note that the URL part of the pattern can be whatever you want to use.

    The obtain_auth_token view will return a JSON response when valid username and password fields are POSTed to the view using form data or JSON:

    @@ -508,6 +511,8 @@ class ExampleAuthentication(authentication.BaseAuthentication):

    The HawkREST library builds on the Mohawk library to let you work with Hawk signed requests and responses in your API. Hawk lets two parties securely communicate with each other using messages signed by a shared key. It is based on HTTP MAC access authentication (which was based on parts of OAuth 1.0).

    HTTP Signature Authentication

    HTTP Signature (currently a IETF draft) provides a way to achieve origin authentication and message integrity for HTTP messages. Similar to Amazon's HTTP Signature scheme, used by many of its services, it permits stateless, per-request authentication. Elvio Toccalino maintains the djangorestframework-httpsignature package which provides an easy to use HTTP Signature Authentication mechanism.

    +

    Djoser

    +

    Djoser library provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation. The package works with a custom user model and it uses token based authentication. This is a ready to use REST implementation of Django authentication system.

    diff --git a/api-guide/format-suffixes.html b/api-guide/format-suffixes.html index 47ed1a5c..0531f75c 100644 --- a/api-guide/format-suffixes.html +++ b/api-guide/format-suffixes.html @@ -219,12 +219,13 @@ used all the time.

    Example:

    from rest_framework.urlpatterns import format_suffix_patterns
    +from blog import views
     
    -urlpatterns = patterns('blog.views',
    -    url(r'^/$', 'api_root'),
    -    url(r'^comments/$', 'comment_list'),
    -    url(r'^comments/(?P<pk>[0-9]+)/$', 'comment_detail')
    -)
    +urlpatterns = [
    +    url(r'^/$', views.apt_root),
    +    url(r'^comments/$', views.comment_list),
    +    url(r'^comments/(?P<pk>[0-9]+)/$', views.comment_detail)
    +]
     
     urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
     
    diff --git a/api-guide/metadata.html b/api-guide/metadata.html new file mode 100644 index 00000000..1ca656b7 --- /dev/null +++ b/api-guide/metadata.html @@ -0,0 +1,324 @@ + + + + + Metadata - Django REST framework + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    + + + + +
    + + + +
    +

    metadata.py

    +

    Metadata

    +
    +

    [The OPTIONS] method allows a client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.

    +

    RFC7231, Section 4.3.7.

    +
    +

    REST framework includes a configurable mechanism for determining how your API should respond to OPTIONS requests. This allows you to return API schema or other resource information.

    +

    There are not currently any widely adopted conventions for exactly what style of response should be returned for HTTP OPTIONS requests, so we provide an ad-hoc style that returns some useful information.

    +

    Here's an example response that demonstrates the information that is returned by default.

    +
    HTTP 200 OK
    +Allow: GET, POST, HEAD, OPTIONS
    +Content-Type: application/json
    +
    +{
    +    "name": "To Do List",
    +    "description": "List existing 'To Do' items, or create a new item.",
    +    "renders": [
    +        "application/json",
    +        "text/html"
    +    ],
    +    "parses": [
    +        "application/json",
    +        "application/x-www-form-urlencoded",
    +        "multipart/form-data"
    +    ],
    +    "actions": {
    +        "POST": {
    +            "note": {
    +                "type": "string",
    +                "required": false,
    +                "read_only": false,
    +                "label": "title",
    +                "max_length": 100
    +            }
    +        }
    +    }
    +}
    +
    +

    Setting the metadata scheme

    +

    You can set the metadata class globally using the 'DEFAULT_METADATA_CLASS' settings key:

    +
    REST_FRAMEWORK = {
    +    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
    +}
    +
    +

    Or you can set the metadata class individually for a view:

    +
    class APIRoot(APIView):
    +    metadata_class = APIRootMetadata
    +
    +    def get(self, request, format=None):
    +        return Response({
    +            ...
    +        })
    +
    +

    The REST framework package only includes a single metadata class implementation, named SimpleMetadata. If you want to use an alternative style you'll need to implement a custom metadata class.

    +

    Creating schema endpoints

    +

    If you have specific requirements for creating schema endpoints that are accessed with regular GET requests, you might consider re-using the metadata API for doing so.

    +

    For example, the following additional route could be used on a viewset to provide a linkable schema endpoint.

    +
    @list_route(methods=['GET'])
    +def schema(self, request):
    +    meta = self.metadata_class()
    +    data = meta.determine_metadata(request, self)
    +    return Response(data)
    +
    +

    There are a couple of reasons that you might choose to take this approach, including that OPTIONS responses are not cacheable.

    +
    +

    Custom metadata classes

    +

    If you want to provide a custom metadata class you should override BaseMetadata and implement the determine_metadata(self, request, view) method.

    +

    Useful things that you might want to do could include returning schema information, using a format such as JSON schema, or returning debug information to admin users.

    +

    Example

    +

    The following class could be used to limit the information that is returned to OPTIONS requests.

    +
    class MinimalMetadata(BaseMetadata):
    +    """
    +    Don't include field and other information for `OPTIONS` requests.
    +    Just return the name and description.
    +    """
    +    def determine_metadata(self, request, view):
    +        return {
    +            'name': view.get_view_name(),
    +            'description': view.get_view_description()
    +        }
    +
    +
    +
    +
    +
    + +
    +
    + + + + + + + + + + + diff --git a/api-guide/validators.html b/api-guide/validators.html new file mode 100644 index 00000000..de19736e --- /dev/null +++ b/api-guide/validators.html @@ -0,0 +1,388 @@ + + + + + Validators - Django REST framework + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    + + + + +
    + + + +
    +

    validators.py

    +

    Validators

    +
    +

    Validators can be useful for re-using validation logic between different types of fields.

    +

    Django documentation

    +
    +

    Most of the time you're dealing with validation in REST framework you'll simply be relying on the default field validation, or writing explicit validation methods on serializer or field classes.

    +

    Sometimes you'll want to place your validation logic into reusable components, so that it can easily be reused throughout your codebase. This can be achieved by using validator functions and validator classes.

    +

    Validation in REST framework

    +

    Validation in Django REST framework serializers is handled a little differently to how validation works in Django's ModelForm class.

    +

    With ModelForm the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class. This is advantageous for the following reasons:

    +
      +
    • It introduces a proper separation of concerns, making your code behaviour more obvious.
    • +
    • It is easy to switch between using shortcut ModelSerializer classes and using explicit Serializer classes. Any validation behaviour being used for ModelSerializer is simple to replicate.
    • +
    • Printing the repr of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behaviour being called on the model instance.
    • +
    +

    When you're using ModelSerializer all of this is handled automatically for you. If you want to drop down to using a Serializer classes instead, then you need to define the validation rules explicitly.

    +

    Example

    +

    As an example of how REST framework uses explicit validation, we'll take a simple model class that has a field with a uniqueness constraint.

    +
    class CustomerReportRecord(models.Model):
    +    time_raised = models.DateTimeField(default=timezone.now, editable=False) 
    +    reference = models.CharField(unique=True, max_length=20)
    +    description = models.TextField()
    +
    +

    Here's a basic ModelSerializer that we can use for creating or updating instances of CustomerReportRecord:

    +
    class CustomerReportSerializer(serializers.ModelSerializer):
    +    class Meta:
    +        model = CustomerReportRecord
    +
    +

    If we open up the Django shell using manage.py shell we can now

    +
    >>> from project.example.serializers import CustomerReportSerializer
    +>>> serializer = CustomerReportSerializer()
    +>>> print(repr(serializer))
    +CustomerReportSerializer():
    +    id = IntegerField(label='ID', read_only=True)
    +    time_raised = DateTimeField(read_only=True)
    +    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])
    +    description = CharField(style={'type': 'textarea'})
    +
    +

    The interesting bit here is the reference field. We can see that the uniqueness constraint is being explicitly enforced by a validator on the serializer field.

    +

    Because of this more explicit style REST framework includes a few validator classes that are not available in core Django. These classes are detailed below.

    +
    +

    UniqueValidator

    +

    This validator can be used to enforce the unique=True constraint on model fields. +It takes a single required argument, and an optional messages argument:

    +
      +
    • queryset required - This is the queryset against which uniqueness should be enforced.
    • +
    • message - The error message that should be used when validation fails.
    • +
    +

    This validator should be applied to serializer fields, like so:

    +
    slug = SlugField(
    +    max_length=100,
    +    validators=[UniqueValidator(queryset=BlogPost.objects.all())]
    +)
    +
    +

    UniqueTogetherValidator

    +

    This validator can be used to enforce unique_together constraints on model instances. +It has two required arguments, and a single optional messages argument:

    +
      +
    • queryset required - This is the queryset against which uniqueness should be enforced.
    • +
    • fields required - A list or tuple of field names which should make a unique set. These must exist as fields on the serializer class.
    • +
    • message - The error message that should be used when validation fails.
    • +
    +

    The validator should be applied to serializer classes, like so:

    +
    class ExampleSerializer(serializers.Serializer):
    +    # ...
    +    class Meta:
    +        # ToDo items belong to a parent list, and have an ordering defined
    +        # by the 'position' field. No two items in a given list may share
    +        # the same position.
    +        validators = [
    +            UniqueTogetherValidator(
    +                queryset=ToDoItem.objects.all(),
    +                fields=('list', 'position')
    +            )
    +        ]
    +
    +

    UniqueForDateValidator

    +

    UniqueForMonthValidator

    +

    UniqueForYearValidator

    +

    These validators can be used to enforce the unique_for_date, unique_for_month and unique_for_year constraints on model instances. They take the following arguments:

    +
      +
    • queryset required - This is the queryset against which uniqueness should be enforced.
    • +
    • field required - A field name against which uniqueness in the given date range will be validated. This must exist as a field on the serializer class.
    • +
    • date_field required - A field name which will be used to determine date range for the uniqueness constrain. This must exist as a field on the serializer class.
    • +
    • message - The error message that should be used when validation fails.
    • +
    +

    The validator should be applied to serializer classes, like so:

    +
    class ExampleSerializer(serializers.Serializer):
    +    # ...
    +    class Meta:
    +        # Blog posts should have a slug that is unique for the current year.
    +        validators = [
    +            UniqueForYearValidator(
    +                queryset=BlogPostItem.objects.all(),
    +                field='slug',
    +                date_field='published'
    +            )
    +        ]
    +
    +

    The date field that is used for the validation is always required to be present on the serializer class. You can't simply rely on a model class default=..., because the value being used for the default wouldn't be generated until after the validation has run.

    +

    There are a couple of styles you may want to use for this depending on how you want your API to behave. If you're using ModelSerializer you'll probably simply rely on the defaults that REST framework generates for you, but if you are using Serializer or simply want more explicit control, use on of the styles demonstrated below.

    +

    Using with a writable date field.

    +

    If you want the date field to be writable the only thing worth noting is that you should ensure that it is always available in the input data, either by setting a default argument, or by setting required=True.

    +
    published = serializers.DateTimeField(required=True)
    +
    +

    Using with a read-only date field.

    +

    If you want the date field to be visible, but not editable by the user, then set read_only=True and additionally set a default=... argument.

    +
    published = serializers.DateTimeField(read_only=True, default=timezone.now)
    +
    +

    The field will not be writable to the user, but the default value will still be passed through to the validated_data.

    +

    Using with a hidden date field.

    +

    If you want the date field to be entirely hidden from the user, then use HiddenField. This field type does not accept user input, but instead always returns it's default value to the validated_data in the serializer.

    +
    published = serializers.HiddenField(default=timezone.now)
    +
    +
    +

    Writing custom validators

    +

    You can use any of Django's existing validators, or write your own custom validators.

    +

    Function based

    +

    A validator may be any callable that raises a serializers.ValidationError on failure.

    +
    def even_number(value):
    +    if value % 2 != 0:
    +        raise serializers.ValidationError('This field must be an even number.')
    +
    +

    Class based

    +

    To write a class based validator, use the __call__ method. Class based validators are useful as they allow you to parameterize and reuse behavior.

    +
    class MultipleOf:
    +    def __init__(self, base):
    +        self.base = base
    +
    +    def __call__(self, value):
    +        if value % self.base != 0
    +            message = 'This field must be a multiple of %d.' % self.base
    +            raise serializers.ValidationError(message)
    +
    +

    Using set_context()

    +

    In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a set_context method on a class based validator.

    +
    def set_context(self, serializer_field):
    +    # Determine if this is an update or a create operation.
    +    # In `__call__` we can then use that information to modify the validation behavior.
    +    self.is_update = serializer_field.parent.instance is not None
    +
    +
    +
    +
    +
    + +
    +
    + + + + + + + + + + + -- cgit v1.2.3