mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 13:04:03 +03:00
Rename HTMLTemplateRenderer -> HTMLRenderer, DocuemntingHTMLRenderer -> BrowseableAPIRenderer
This commit is contained in:
parent
ce21fa1dc6
commit
97a7f27c82
|
@ -53,7 +53,8 @@ To run the tests.
|
|||
|
||||
## 2.0.0
|
||||
|
||||
Redesign of core components.
|
||||
* Redesign of core components.
|
||||
* Fix **all of the things**.
|
||||
|
||||
# License
|
||||
|
||||
|
|
|
@ -16,13 +16,48 @@ Serializer fields handle converting between primative values and internal dataty
|
|||
|
||||
# Generic Fields
|
||||
|
||||
These generic fields are used for representing arbitrary model fields or the output of model methods.
|
||||
|
||||
## Field
|
||||
|
||||
A generic, read-only field. You can use this field for any attribute that does not need to support write operations.
|
||||
A generic, **read-only** field. You can use this field for any attribute that does not need to support write operations.
|
||||
|
||||
For example, using the following model.
|
||||
|
||||
class Account(models.Model):
|
||||
owner = models.ForeignKey('auth.user')
|
||||
name = models.CharField(max_length=100)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
payment_expiry = models.DateTimeField()
|
||||
|
||||
def has_expired(self):
|
||||
now = datetime.datetime.now()
|
||||
return now > self.payment_expiry
|
||||
|
||||
A serializer definition that looked like this:
|
||||
|
||||
class AccountSerializer(serializers.HyperlinkedModelSerializer):
|
||||
expired = Field(source='has_expired')
|
||||
|
||||
class Meta:
|
||||
fields = ('url', 'owner', 'name', 'expired')
|
||||
|
||||
Would produced output similar to:
|
||||
|
||||
{
|
||||
'url': 'http://example.com/api/accounts/3/',
|
||||
'owner': 'http://example.com/api/users/12/',
|
||||
'name': 'FooCorp business account',
|
||||
'expired': True
|
||||
}
|
||||
|
||||
Be default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when neccesary.
|
||||
|
||||
You can customize this behaviour by overriding the `.to_native(self, value)` method.
|
||||
|
||||
## WritableField
|
||||
|
||||
A field that supports both read and
|
||||
A field that supports both read and write operations. By itself `WriteableField` does not perform any translation of input values into a given type. You won't typically use this field directly, but you may want to override it and implement the `.to_native(self, value)` and `.from_native(self, value)` methods.
|
||||
|
||||
## ModelField
|
||||
|
||||
|
@ -56,10 +91,86 @@ These fields represent basic datatypes, and support both reading and writing val
|
|||
|
||||
Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`.
|
||||
|
||||
## RelatedField
|
||||
|
||||
This field can be applied to any of the following:
|
||||
|
||||
* A `ForeignKey` field.
|
||||
* A `OneToOneField` field.
|
||||
* A reverse OneToOne relationship
|
||||
* Any other "to-one" relationship.
|
||||
|
||||
By default `RelatedField` will represent the target of the field using it's `__unicode__` method.
|
||||
|
||||
You can customise this behaviour by subclassing `ManyRelatedField`, and overriding the `.to_native(self, value)` method.
|
||||
|
||||
## ManyRelatedField
|
||||
|
||||
This field can be applied to any of the following:
|
||||
|
||||
* A `ManyToManyField` field.
|
||||
* A reverse ManyToMany relationship.
|
||||
* A reverse ForeignKey relationship
|
||||
* Any other "to-many" relationship.
|
||||
|
||||
By default `ManyRelatedField` will represent the targets of the field using their `__unicode__` method.
|
||||
|
||||
For example, given the following models:
|
||||
|
||||
class TaggedItem(models.Model):
|
||||
"""
|
||||
Tags arbitrary model instances using a generic relation.
|
||||
|
||||
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
|
||||
"""
|
||||
tag = models.SlugField()
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.tag
|
||||
|
||||
|
||||
class Bookmark(models.Model):
|
||||
"""
|
||||
A bookmark consists of a URL, and 0 or more descriptive tags.
|
||||
"""
|
||||
url = models.URLField()
|
||||
tags = GenericRelation(TaggedItem)
|
||||
|
||||
And a model serializer defined like this:
|
||||
|
||||
class BookmarkSerializer(serializers.ModelSerializer):
|
||||
tags = serializers.ManyRelatedField(source='tags')
|
||||
|
||||
class Meta:
|
||||
model = Bookmark
|
||||
exclude = ('id',)
|
||||
|
||||
The an example output format for a Bookmark instance would be:
|
||||
|
||||
{
|
||||
'tags': [u'django', u'python'],
|
||||
'url': u'https://www.djangoproject.com/'
|
||||
}
|
||||
|
||||
## PrimaryKeyRelatedField
|
||||
|
||||
As with `RelatedField` field can be applied to any "to-one" relationship, such as a `ForeignKey` field.
|
||||
|
||||
`PrimaryKeyRelatedField` will represent the target of the field using it's primary key.
|
||||
|
||||
Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag.
|
||||
|
||||
## ManyPrimaryKeyRelatedField
|
||||
|
||||
As with `RelatedField` field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship.
|
||||
|
||||
`PrimaryKeyRelatedField` will represent the target of the field using their primary key.
|
||||
|
||||
Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `readonly` flag.
|
||||
|
||||
## HyperlinkedRelatedField
|
||||
|
||||
## ManyHyperlinkedRelatedField
|
||||
|
|
|
@ -23,7 +23,7 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERERS`
|
|||
REST_FRAMEWORK = {
|
||||
'DEFAULT_RENDERERS': (
|
||||
'rest_framework.renderers.YAMLRenderer',
|
||||
'rest_framework.renderers.DocumentingHTMLRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -88,20 +88,12 @@ If your API includes views that can serve both regular webpages and API response
|
|||
|
||||
**.format:** `'.xml'`
|
||||
|
||||
## DocumentingHTMLRenderer
|
||||
|
||||
Renders data into HTML for the browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.
|
||||
|
||||
**.media_type:** `text/html`
|
||||
|
||||
**.format:** `'.api'`
|
||||
|
||||
## HTMLTemplateRenderer
|
||||
## HTMLRenderer
|
||||
|
||||
Renders data to HTML, using Django's standard template rendering.
|
||||
Unlike other renderers, the data passed to the `Response` does not need to be serialized. Also, unlike other renderers, you may want to include a `template_name` argument when creating the `Response`.
|
||||
|
||||
The HTMLTemplateRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context.
|
||||
The HTMLRenderer will create a `RequestContext`, using the `response.data` as the context dict, and determine a template name to use to render the context.
|
||||
|
||||
The template name is determined by (in order of preference):
|
||||
|
||||
|
@ -109,18 +101,54 @@ The template name is determined by (in order of preference):
|
|||
2. An explicit `.template_name` attribute set on this class.
|
||||
3. The return result of calling `view.get_template_names()`.
|
||||
|
||||
You can use `HTMLTemplateRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
|
||||
An example of a view that uses `HTMLRenderer`:
|
||||
|
||||
If you're building websites that use `HTMLTemplateRenderer` along with other renderer classes, you should consider listing `HTMLTemplateRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed ACCEPT headers.
|
||||
class UserInstance(generics.RetrieveUserAPIView):
|
||||
"""
|
||||
A view that returns a templated HTML representations of a given user.
|
||||
"""
|
||||
model = Users
|
||||
renderer_classes = (HTMLRenderer,)
|
||||
|
||||
def get(self, request, \*args, **kwargs)
|
||||
self.object = self.get_object()
|
||||
return Response(self.object, template_name='user_detail.html')
|
||||
|
||||
You can use `HTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
|
||||
|
||||
If you're building websites that use `HTMLRenderer` along with other renderer classes, you should consider listing `HTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers.
|
||||
|
||||
**.media_type:** `text/html`
|
||||
|
||||
**.format:** `'.html'`
|
||||
|
||||
## BrowsableAPIRenderer
|
||||
|
||||
Renders data into HTML for the Browseable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.
|
||||
|
||||
**.media_type:** `text/html`
|
||||
|
||||
**.format:** `'.api'`
|
||||
|
||||
## Custom renderers
|
||||
|
||||
To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, media_type)` method.
|
||||
|
||||
For example:
|
||||
|
||||
from django.utils.encoding import smart_unicode
|
||||
from rest_framework import renderers
|
||||
|
||||
|
||||
class PlainText(renderers.BaseRenderer):
|
||||
media_type = 'text/plain'
|
||||
format = 'txt'
|
||||
|
||||
def render(self, data, media_type):
|
||||
if isinstance(data, basestring):
|
||||
return data
|
||||
return smart_unicode(data)
|
||||
|
||||
---
|
||||
|
||||
# Advanced renderer usage
|
||||
|
@ -139,7 +167,7 @@ In some cases you might want your view to use different serialization styles dep
|
|||
For example:
|
||||
|
||||
@api_view(('GET',))
|
||||
@renderer_classes((TemplateHTMLRenderer, JSONRenderer))
|
||||
@renderer_classes((HTMLRenderer, JSONRenderer))
|
||||
def list_users(request):
|
||||
"""
|
||||
A view that can return JSON or HTML representations
|
||||
|
|
|
@ -32,7 +32,7 @@ Arguments:
|
|||
|
||||
* `data`: The serialized data for the response.
|
||||
* `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes].
|
||||
* `template_name`: A template name to use if `HTMLTemplateRenderer` is selected.
|
||||
* `template_name`: A template name to use if `HTMLRenderer` is selected.
|
||||
* `headers`: A dictionary of HTTP headers to use in the response.
|
||||
|
||||
## .render()
|
||||
|
@ -68,7 +68,7 @@ The rendered content of the response. The `.render()` method must have been cal
|
|||
|
||||
## .template_name
|
||||
|
||||
The `template_name`, if supplied. Only required if `HTMLTemplateRenderer` or some other custom template renderer is the accepted renderer for the reponse.
|
||||
The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the reponse.
|
||||
|
||||
## .accepted_renderer
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ Default:
|
|||
|
||||
(
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.DocumentingHTMLRenderer'
|
||||
'rest_framework.renderers.BrowsableAPIRenderer'
|
||||
'rest_framework.renderers.TemplateHTMLRenderer'
|
||||
)
|
||||
|
||||
|
|
|
@ -132,7 +132,7 @@ class YAMLRenderer(BaseRenderer):
|
|||
return yaml.safe_dump(data)
|
||||
|
||||
|
||||
class HTMLTemplateRenderer(BaseRenderer):
|
||||
class HTMLRenderer(BaseRenderer):
|
||||
"""
|
||||
A Base class provided for convenience.
|
||||
|
||||
|
@ -179,7 +179,7 @@ class HTMLTemplateRenderer(BaseRenderer):
|
|||
raise ConfigurationError('Returned a template response with no template_name')
|
||||
|
||||
|
||||
class DocumentingHTMLRenderer(BaseRenderer):
|
||||
class BrowsableAPIRenderer(BaseRenderer):
|
||||
"""
|
||||
HTML renderer used to self-document the API.
|
||||
"""
|
||||
|
@ -197,7 +197,7 @@ class DocumentingHTMLRenderer(BaseRenderer):
|
|||
|
||||
# Find the first valid renderer and render the content. (Don't use another documenting renderer.)
|
||||
renderers = [renderer for renderer in view.renderer_classes
|
||||
if not issubclass(renderer, DocumentingHTMLRenderer)]
|
||||
if not issubclass(renderer, BrowsableAPIRenderer)]
|
||||
if not renderers:
|
||||
return '[No renderers were found]'
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ from django.utils import importlib
|
|||
DEFAULTS = {
|
||||
'DEFAULT_RENDERERS': (
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.DocumentingHTMLRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
),
|
||||
'DEFAULT_PARSERS': (
|
||||
'rest_framework.parsers.JSONParser',
|
||||
|
|
|
@ -3,12 +3,12 @@ from django.test import TestCase
|
|||
from django.template import TemplateDoesNotExist, Template
|
||||
import django.template.loader
|
||||
from rest_framework.decorators import api_view, renderer_classes
|
||||
from rest_framework.renderers import HTMLTemplateRenderer
|
||||
from rest_framework.renderers import HTMLRenderer
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
@api_view(('GET',))
|
||||
@renderer_classes((HTMLTemplateRenderer,))
|
||||
@renderer_classes((HTMLRenderer,))
|
||||
def example(request):
|
||||
"""
|
||||
A view that can returns an HTML representation.
|
||||
|
|
|
@ -9,7 +9,7 @@ from rest_framework.compat import yaml
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
|
||||
XMLRenderer, JSONPRenderer, DocumentingHTMLRenderer
|
||||
XMLRenderer, JSONPRenderer, BrowsableAPIRenderer
|
||||
from rest_framework.parsers import YAMLParser, XMLParser
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
|
@ -68,14 +68,14 @@ class MockGETView(APIView):
|
|||
|
||||
|
||||
class HTMLView(APIView):
|
||||
renderer_classes = (DocumentingHTMLRenderer, )
|
||||
renderer_classes = (BrowsableAPIRenderer, )
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
return Response('text')
|
||||
|
||||
|
||||
class HTMLView1(APIView):
|
||||
renderer_classes = (DocumentingHTMLRenderer, JSONRenderer)
|
||||
renderer_classes = (BrowsableAPIRenderer, JSONRenderer)
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
return Response('text')
|
||||
|
@ -97,7 +97,7 @@ class POSTDeniedPermission(permissions.BasePermission):
|
|||
|
||||
|
||||
class POSTDeniedView(APIView):
|
||||
renderer_classes = (DocumentingHTMLRenderer,)
|
||||
renderer_classes = (BrowsableAPIRenderer,)
|
||||
permission_classes = (POSTDeniedPermission,)
|
||||
|
||||
def get(self, request):
|
||||
|
|
|
@ -9,7 +9,7 @@ from rest_framework import status
|
|||
from rest_framework.renderers import (
|
||||
BaseRenderer,
|
||||
JSONRenderer,
|
||||
DocumentingHTMLRenderer
|
||||
BrowsableAPIRenderer
|
||||
)
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
|
@ -53,14 +53,14 @@ class MockView(APIView):
|
|||
|
||||
|
||||
class HTMLView(APIView):
|
||||
renderer_classes = (DocumentingHTMLRenderer, )
|
||||
renderer_classes = (BrowsableAPIRenderer, )
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
return Response('text')
|
||||
|
||||
|
||||
class HTMLView1(APIView):
|
||||
renderer_classes = (DocumentingHTMLRenderer, JSONRenderer)
|
||||
renderer_classes = (BrowsableAPIRenderer, JSONRenderer)
|
||||
|
||||
def get(self, request, **kwargs):
|
||||
return Response('text')
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import copy
|
||||
import datetime
|
||||
from django.test import TestCase
|
||||
from rest_framework import serializers
|
||||
|
|
Loading…
Reference in New Issue
Block a user