Merge master
12
.travis.yml
|
@ -8,23 +8,23 @@ python:
|
|||
- "3.4"
|
||||
|
||||
env:
|
||||
- DJANGO="https://www.djangoproject.com/download/1.7.b4/tarball/"
|
||||
- DJANGO="https://www.djangoproject.com/download/1.7c2/tarball/"
|
||||
- DJANGO="django==1.6.5"
|
||||
- DJANGO="django==1.5.8"
|
||||
- DJANGO="django==1.4.13"
|
||||
|
||||
install:
|
||||
- pip install $DJANGO
|
||||
- pip install defusedxml==0.3 Pillow==2.3.0
|
||||
- pip install defusedxml==0.3
|
||||
- pip install Pillow==2.3.0
|
||||
- pip install django-guardian==1.2.3
|
||||
- pip install pytest-django==2.6.1
|
||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211; fi"
|
||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.2.4; fi"
|
||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.4; fi"
|
||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-guardian==1.1.1; fi"
|
||||
- "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4; fi"
|
||||
- "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.7; fi"
|
||||
- "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
|
||||
- "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7.b4/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
|
||||
- "if [[ ${DJANGO} == 'https://www.djangoproject.com/download/1.7c2/tarball/' ]]; then pip install -e git+https://github.com/linovia/django-guardian.git@feature/django_1_7#egg=django-guardian-1.2.0; fi"
|
||||
- export PYTHONPATH=.
|
||||
|
||||
script:
|
||||
|
@ -33,7 +33,7 @@ script:
|
|||
matrix:
|
||||
exclude:
|
||||
- python: "2.6"
|
||||
env: DJANGO="https://www.djangoproject.com/download/1.7.b4/tarball/"
|
||||
env: DJANGO="https://www.djangoproject.com/download/1.7c2/tarball/"
|
||||
- python: "3.2"
|
||||
env: DJANGO="django==1.4.13"
|
||||
- python: "3.3"
|
||||
|
|
12
README.md
|
@ -1,13 +1,3 @@
|
|||
---
|
||||
|
||||
#### Django REST framework 3 - Kickstarter announcement!
|
||||
|
||||
We are currently running a Kickstarter campaign to help fund the development of Django REST framework 3.
|
||||
|
||||
If you want to help drive sustainable open-source development forward, then **please check out [the Kickstarter project](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3) and consider funding us.**
|
||||
|
||||
---
|
||||
|
||||
# Django REST framework
|
||||
|
||||
[![build-status-image]][travis]
|
||||
|
@ -18,7 +8,7 @@ If you want to help drive sustainable open-source development forward, then **pl
|
|||
|
||||
# Overview
|
||||
|
||||
Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.
|
||||
Django REST framework is a powerful and flexible toolkit for building Web APIs.
|
||||
|
||||
Some reasons you might want to use REST framework:
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ A dictionary of error codes to error messages.
|
|||
### `widget`
|
||||
|
||||
Used only if rendering the field to HTML.
|
||||
This argument sets the widget that should be used to render the field.
|
||||
This argument sets the widget that should be used to render the field. For more details, and a list of available widgets, see [the Django documentation on form widgets][django-widgets].
|
||||
|
||||
### `label`
|
||||
|
||||
|
@ -362,12 +362,17 @@ The [drf-compound-fields][drf-compound-fields] package provides "compound" seria
|
|||
|
||||
The [drf-extra-fields][drf-extra-fields] package provides extra serializer fields for REST framework, including `Base64ImageField` and `PointField` classes.
|
||||
|
||||
## django-rest-framework-gis
|
||||
|
||||
The [django-rest-framework-gis][django-rest-framework-gis] package provides geographic addons for django rest framework like a `GeometryField` field and a GeoJSON serializer.
|
||||
|
||||
|
||||
[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data
|
||||
[FILE_UPLOAD_HANDLERS]: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS
|
||||
[ecma262]: http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
|
||||
[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
|
||||
[django-widgets]: https://docs.djangoproject.com/en/dev/ref/forms/widgets/
|
||||
[iso8601]: http://www.w3.org/TR/NOTE-datetime
|
||||
[drf-compound-fields]: http://drf-compound-fields.readthedocs.org
|
||||
[drf-extra-fields]: https://github.com/Hipo/drf-extra-fields
|
||||
[django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis
|
||||
|
|
|
@ -43,6 +43,12 @@ For more complex cases you might also want to override various methods on the vi
|
|||
return 20
|
||||
return 100
|
||||
|
||||
def list(self, request):
|
||||
# Note the use of `get_queryset()` instead of `self.queryset`
|
||||
queryset = self.get_queryset()
|
||||
serializer = UserSerializer(queryset, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something the following entry.
|
||||
|
||||
url(r'^/users/', ListCreateAPIView.as_view(model=User), name='user-list')
|
||||
|
@ -63,7 +69,7 @@ Each of the concrete generic views provided is built by combining `GenericAPIVie
|
|||
|
||||
The following attributes control the basic view behavior.
|
||||
|
||||
* `queryset` - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the `get_queryset()` method.
|
||||
* `queryset` - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the `get_queryset()` method. If you are overriding a view method, it is important that you call `get_queryset()` instead of accessing this property directly, as `queryset` will get evaluated once, and those results will be cached for all subsequent requests.
|
||||
* `serializer_class` - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, 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'`. Note that when using hyperlinked APIs you'll need to ensure that *both* the API views *and* the serializer classes set the lookup fields if you need to use a custom value.
|
||||
* `lookup_url_kwarg` - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as `lookup_field`.
|
||||
|
@ -93,6 +99,8 @@ The following attributes are used to control pagination when used with list view
|
|||
|
||||
Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the `queryset` attribute, or the default queryset for the model if the `model` shortcut is being used.
|
||||
|
||||
This method should always be used rather than accessing `self.queryset` directly, as `self.queryset` gets evaluated only once, and those results are cached for all subsequent requests.
|
||||
|
||||
May be overridden to provide dynamic behavior such as returning a queryset that is specific to the user making the request.
|
||||
|
||||
For example:
|
||||
|
|
|
@ -244,7 +244,7 @@ The [REST Condition][rest-condition] package is another extension for building c
|
|||
[authentication]: authentication.md
|
||||
[throttling]: throttling.md
|
||||
[filtering]: filtering.md
|
||||
[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions
|
||||
[contribauth]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#custom-permissions
|
||||
[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
|
||||
[guardian]: https://github.com/lukaszb/django-guardian
|
||||
[get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user
|
||||
|
|
|
@ -580,7 +580,21 @@ The following custom model serializer could be used as a base class for model se
|
|||
def get_pk_field(self, model_field):
|
||||
return None
|
||||
|
||||
---
|
||||
|
||||
# Third party packages
|
||||
|
||||
The following third party packages are also available.
|
||||
|
||||
## MongoengineModelSerializer
|
||||
|
||||
The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework.
|
||||
|
||||
## GeoFeatureModelSerializer
|
||||
|
||||
The [django-rest-framework-gis][django-rest-framework-gis] package provides a `GeoFeatureModelSerializer` serializer class that supports GeoJSON both for read and write operations.
|
||||
|
||||
[cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion
|
||||
[relations]: relations.md
|
||||
[mongoengine]: https://github.com/umutbozkurt/django-rest-framework-mongoengine
|
||||
[django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis
|
||||
|
|
|
@ -58,7 +58,7 @@ using the `APIView` class based views.
|
|||
|
||||
Or, if you're using the `@api_view` decorator with function based views.
|
||||
|
||||
@api_view('GET')
|
||||
@api_view(['GET'])
|
||||
@throttle_classes([UserRateThrottle])
|
||||
def example_view(request, format=None):
|
||||
content = {
|
||||
|
|
|
@ -307,3 +307,76 @@ table {
|
|||
.side-nav {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
|
||||
ul.sponsor.diamond li a {
|
||||
float: left;
|
||||
width: 600px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 70px;
|
||||
padding: 300px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 600px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
ul.sponsor.diamond li a {
|
||||
float: left;
|
||||
width: 300px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 40px;
|
||||
padding: 300px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 280px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 150%;
|
||||
}
|
||||
}
|
||||
|
||||
ul.sponsor.platinum li a {
|
||||
float: left;
|
||||
width: 300px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 40px;
|
||||
padding: 300px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 280px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
ul.sponsor.gold li a {
|
||||
float: left;
|
||||
width: 130px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 30px;
|
||||
padding: 150px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 130px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
ul.sponsor.silver li a {
|
||||
float: left;
|
||||
width: 130px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 30px;
|
||||
padding: 150px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 130px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
ul.sponsor {
|
||||
list-style: none;
|
||||
display: block;
|
||||
}
|
||||
|
|
BIN
docs/img/sponsors/0-eventbrite.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/img/sponsors/1-cyan.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
docs/img/sponsors/1-divio.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
docs/img/sponsors/1-kuwaitnet.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/sponsors/1-lulu.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/sponsors/1-potato.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/sponsors/1-purplebit.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
docs/img/sponsors/1-runscope.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/sponsors/1-simple-energy.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
docs/img/sponsors/1-vokal_interactive.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/img/sponsors/1-wiredrive.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
docs/img/sponsors/2-byte.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/sponsors/2-compile.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/img/sponsors/2-crate.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
docs/img/sponsors/2-cryptico.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/img/sponsors/2-django.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
docs/img/sponsors/2-galileo_press.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/sponsors/2-heroku.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
docs/img/sponsors/2-hipflask.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/img/sponsors/2-hipo.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
docs/img/sponsors/2-koordinates.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
docs/img/sponsors/2-laterpay.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
docs/img/sponsors/2-lightning_kite.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
docs/img/sponsors/2-mirus_research.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/sponsors/2-nexthub.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
docs/img/sponsors/2-opbeat.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/sponsors/2-prorenata.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
docs/img/sponsors/2-rapasso.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/sponsors/2-schuberg_philis.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/img/sponsors/2-security_compass.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
docs/img/sponsors/2-sga.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/sponsors/2-sirono.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
docs/img/sponsors/2-vinta.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/img/sponsors/3-aba.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/sponsors/3-aditium.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/img/sponsors/3-alwaysdata.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
docs/img/sponsors/3-ax_semantics.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/sponsors/3-beefarm.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/sponsors/3-blimp.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
docs/img/sponsors/3-brightloop.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/img/sponsors/3-cantemo.gif
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
docs/img/sponsors/3-crosswordtracker.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
docs/img/sponsors/3-fluxility.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
docs/img/sponsors/3-garfo.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
docs/img/sponsors/3-gizmag.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
docs/img/sponsors/3-holvi.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
docs/img/sponsors/3-imt_computer_services.png
Normal file
After Width: | Height: | Size: 69 KiB |
BIN
docs/img/sponsors/3-infinite_code.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
docs/img/sponsors/3-ipushpull.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
docs/img/sponsors/3-isl.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
docs/img/sponsors/3-life_the_game.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
docs/img/sponsors/3-makespace.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
docs/img/sponsors/3-nephila.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
docs/img/sponsors/3-openeye.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/img/sponsors/3-pathwright.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/sponsors/3-phurba.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
docs/img/sponsors/3-pkgfarm.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
docs/img/sponsors/3-providenz.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/sponsors/3-safari.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/sponsors/3-shippo.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
docs/img/sponsors/3-teonite.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
docs/img/sponsors/3-thermondo-gmbh.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
docs/img/sponsors/3-tivix.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
docs/img/sponsors/3-trackmaven.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
docs/img/sponsors/3-transcode.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
docs/img/sponsors/3-triggered_messaging.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
docs/img/sponsors/3-vzzual.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/sponsors/3-wildfish.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
|
@ -9,14 +9,6 @@
|
|||
|
||||
---
|
||||
|
||||
#### Django REST framework 3 - Kickstarter announcement!
|
||||
|
||||
We are currently running a Kickstarter campaign to help fund the development of Django REST framework 3.
|
||||
|
||||
If you want to help drive sustainable open-source development **please [check out the Kickstarter project](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3) and consider funding us.**
|
||||
|
||||
---
|
||||
|
||||
<p>
|
||||
<h1 style="position: absolute;
|
||||
width: 1px;
|
||||
|
|
|
@ -69,6 +69,7 @@ For more specific CSS tweaks than simply overriding the default bootstrap theme
|
|||
|
||||
All of the blocks available in the browsable API base template that can be used in your `api.html`.
|
||||
|
||||
* `body` - The entire html `<body>`.
|
||||
* `bodyclass` - Class attribute for the `<body>` tag, empty by default.
|
||||
* `bootstrap_theme` - CSS for the Bootstrap theme.
|
||||
* `bootstrap_navbar_variant` - CSS class for the navbar.
|
||||
|
@ -167,10 +168,10 @@ You can now add the `autocomplete_light.ChoiceWidget` widget to the serializer f
|
|||
[bootstrap]: http://getbootstrap.com
|
||||
[cerulean]: ../img/cerulean.png
|
||||
[slate]: ../img/slate.png
|
||||
[bcustomize]: http://twitter.github.com/bootstrap/customize.html#variables
|
||||
[bcustomize]: http://getbootstrap.com/2.3.2/customize.html
|
||||
[bswatch]: http://bootswatch.com/
|
||||
[bcomponents]: http://twitter.github.com/bootstrap/components.html
|
||||
[bcomponentsnav]: http://twitter.github.com/bootstrap/components.html#navbar
|
||||
[bcomponents]: http://getbootstrap.com/2.3.2/components.html
|
||||
[bcomponentsnav]: http://getbootstrap.com/2.3.2/components.html#navbar
|
||||
[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
|
||||
[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
|
||||
[django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install
|
||||
|
|
|
@ -95,7 +95,7 @@ You can modify the response behavior to `OPTIONS` requests by overriding the `me
|
|||
|
||||
To be fully RESTful an API should present its available actions as hypermedia controls in the responses that it sends.
|
||||
|
||||
In this approach, rather than documenting the available API endpoints up front, the description instead concentrates on the *media types* that are used. The available actions take may be taken on any given URL are not strictly fixed, but are instead made available by the presence of link and form controls in the returned document.
|
||||
In this approach, rather than documenting the available API endpoints up front, the description instead concentrates on the *media types* that are used. The available actions that may be taken on any given URL are not strictly fixed, but are instead made available by the presence of link and form controls in the returned document.
|
||||
|
||||
To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats.
|
||||
|
||||
|
|
|
@ -29,3 +29,134 @@ I can't wait to see where this takes us!
|
|||
Many thanks to everyone for your support so far,
|
||||
|
||||
Tom Christie :)
|
||||
|
||||
---
|
||||
|
||||
## Sponsors
|
||||
|
||||
We've now blazed way past all our goals, with a staggering £30,000 (~$50,000), meaning I'll be in a position to work on the project significantly beyond what we'd originally planned for. I owe a huge debt of gratitude to all the wonderful companies and individuals who have been backing the project so generously, and making this possible.
|
||||
|
||||
---
|
||||
|
||||
### Platinum sponsors
|
||||
|
||||
Our platinum sponsors have each made a hugely substantial contribution to the future development of Django REST framework, and I simply can't thank them enough.
|
||||
|
||||
<ul class="sponsor diamond">
|
||||
<li><a href="https://www.eventbrite.com/" rel="nofollow" style="background-image:url(../img/sponsors/0-eventbrite.png);">Eventbrite</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="sponsor platinum">
|
||||
<li><a href="https://www.divio.ch/" rel="nofollow" style="background-image:url(../img/sponsors/1-divio.png);">Divio</a></li>
|
||||
<li><a href="http://company.onlulu.com/en/" rel="nofollow" style="background-image:url(../img/sponsors/1-lulu.png);">Lulu</a></li>
|
||||
<li><a href="https://p.ota.to/" rel="nofollow" style="background-image:url(../img/sponsors/1-potato.png);">Potato</a></li>
|
||||
<li><a href="http://www.wiredrive.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-wiredrive.png);">Wiredrive</a></li>
|
||||
<li><a href="http://www.cyaninc.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-cyan.png);">Cyan</a></li>
|
||||
<li><a href="https://www.runscope.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-runscope.png);">Runscope</a></li>
|
||||
<li><a href="http://simpleenergy.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-simple-energy.png);">Simple Energy</a></li>
|
||||
<li><a href="http://vokalinteractive.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-vokal_interactive.png);">VOKAL Interactive</a></li>
|
||||
<li><a href="http://www.purplebit.com/" rel="nofollow" style="background-image:url(../img/sponsors/1-purplebit.png);">Purple Bit</a></li>
|
||||
<li><a href="http://www.kuwaitnet.net/" rel="nofollow" style="background-image:url(../img/sponsors/1-kuwaitnet.png);">KuwaitNET</a></li>
|
||||
</ul>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
|
||||
---
|
||||
|
||||
### Gold sponsors
|
||||
|
||||
Our gold sponsors include companies large and small. Many thanks for their significant funding of the project and their commitment to sustainable open-source development.
|
||||
|
||||
<ul class="sponsor gold">
|
||||
<li><a href="https://laterpay.net/" rel="nofollow" style="background-image:url(../img/sponsors/2-laterpay.png);">LaterPay</a></li>
|
||||
<li><a href="https://www.schubergphilis.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-schuberg_philis.png);">Schuberg Philis</a></li>
|
||||
<li><a href="http://prorenata.se/" rel="nofollow" style="background-image:url(../img/sponsors/2-prorenata.png);">ProReNata AB</a></li>
|
||||
<li><a href="https://www.sgawebsites.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-sga.png);">SGA Websites</a></li>
|
||||
<li><a href="http://www.sirono.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-sirono.png);">Sirono</a></li>
|
||||
<li><a href="http://www.vinta.com.br/" rel="nofollow" style="background-image:url(../img/sponsors/2-vinta.png);">Vinta Software Studio</a></li>
|
||||
<li><a href="http://www.rapasso.nl/index.php/en" rel="nofollow" style="background-image:url(../img/sponsors/2-rapasso.png);">Rapasso</a></li>
|
||||
<li><a href="https://mirusresearch.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-mirus_research.png);">Mirus Research</a></li>
|
||||
<li><a href="http://hipolabs.com" rel="nofollow" style="background-image:url(../img/sponsors/2-hipo.png);">Hipo</a></li>
|
||||
<li><a href="http://www.byte.nl" rel="nofollow" style="background-image:url(../img/sponsors/2-byte.png);">Byte</a></li>
|
||||
<li><a href="http://lightningkite.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-lightning_kite.png);">Lightning Kite</a></li>
|
||||
<li><a href="https://opbeat.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-opbeat.png);">Opbeat</a></li>
|
||||
<li><a href="https://koordinates.com" rel="nofollow" style="background-image:url(../img/sponsors/2-koordinates.png);">Koordinates</a></li>
|
||||
<li><a href="https://www.heroku.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-heroku.png);">Heroku</a></li>
|
||||
<li><a href="https://www.galileo-press.de/" rel="nofollow" style="background-image:url(../img/sponsors/2-galileo_press.png);">Galileo Press</a></li>
|
||||
<li><a href="http://www.securitycompass.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-security_compass.png);">Security Compass</a></li>
|
||||
<li><a href="https://www.djangoproject.com/foundation/" rel="nofollow" style="background-image:url(../img/sponsors/2-django.png);">Django Software Foundation</a></li>
|
||||
<li><a href="http://www.hipflaskapp.com" rel="nofollow" style="background-image:url(../img/sponsors/2-hipflask.png);">Hipflask</a></li>
|
||||
<li><a href="http://www.crate.io/" rel="nofollow" style="background-image:url(../img/sponsors/2-crate.png);">Crate</a></li>
|
||||
<li><a href="http://crypticocorp.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-cryptico.png);">Cryptico Corp</a></li>
|
||||
<li><a href="http://www.nexthub.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-nexthub.png);">NextHub</a></li>
|
||||
<li><a href="https://www.compile.com/" rel="nofollow" style="background-image:url(../img/sponsors/2-compile.png);">Compile</a></li>
|
||||
<li><a href="http://envisionlinux.org/blog" rel="nofollow">Envision Linux</a></li>
|
||||
</ul>
|
||||
|
||||
<div style="clear: both; padding-bottom: 40px;"></div>
|
||||
|
||||
**Individual backers**: Xitij Ritesh Patel, Howard Sandford, Simon Haugk.
|
||||
|
||||
---
|
||||
|
||||
### Silver sponsors
|
||||
|
||||
The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank you to individuals who have choosen to privately support the project at this level.
|
||||
|
||||
<ul class="sponsor silver">
|
||||
<li><a href="http://www.imtapps.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-imt_computer_services.png);">IMT Computer Services</a></li>
|
||||
<li><a href="http://wildfish.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-wildfish.png);">Wildfish</a></li>
|
||||
<li><a href="http://www.thermondo.de/" rel="nofollow" style="background-image:url(../img/sponsors/3-thermondo-gmbh.png);">Thermondo GmbH</a></li>
|
||||
<li><a href="http://providenz.fr/" rel="nofollow" style="background-image:url(../img/sponsors/3-providenz.png);">Providenz</a></li>
|
||||
<li><a href="https://www.alwaysdata.com" rel="nofollow" style="background-image:url(../img/sponsors/3-alwaysdata.png);">alwaysdata.com</a></li>
|
||||
<li><a href="http://www.triggeredmessaging.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-triggered_messaging.png);">Triggered Messaging</a></li>
|
||||
<li><a href="https://www.ipushpull.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-ipushpull.png);">PushPull Technology Ltd</a></li>
|
||||
<li><a href="http://www.transcode.de/" rel="nofollow" style="background-image:url(../img/sponsors/3-transcode.png);">Transcode</a></li>
|
||||
<li><a href="https://garfo.io/" rel="nofollow" style="background-image:url(../img/sponsors/3-garfo.png);">Garfo</a></li>
|
||||
<li><a href="https://goshippo.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-shippo.png);">Shippo</a></li>
|
||||
<li><a href="http://www.gizmag.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-gizmag.png);">Gizmag</a></li>
|
||||
<li><a href="http://www.tivix.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-tivix.png);">Tivix</a></li>
|
||||
<li><a href="http://www.safaribooksonline.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-safari.png);">Safari</a></li>
|
||||
<li><a href="http://brightloop.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-brightloop.png);">Bright Loop</a></li>
|
||||
<li><a href="http://www.aba-systems.com.au/" rel="nofollow" style="background-image:url(../img/sponsors/3-aba.png);">ABA Systems</a></li>
|
||||
<li><a href="http://beefarm.ru/" rel="nofollow" style="background-image:url(../img/sponsors/3-beefarm.png);">beefarm.ru</a></li>
|
||||
<li><a href="http://www.vzzual.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-vzzual.png);">Vzzual.com</a></li>
|
||||
<li><a href="http://infinite-code.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-infinite_code.png);">Infinite Code</a></li>
|
||||
<li><a href="http://crosswordtracker.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-crosswordtracker.png);">Crossword Tracker</a></li>
|
||||
<li><a href="https://www.pkgfarm.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-pkgfarm.png);">PkgFarm</a></li>
|
||||
<li><a href="http://life.tl/" rel="nofollow" style="background-image:url(../img/sponsors/3-life_the_game.png);">Life. The Game.</a></li>
|
||||
<li><a href="http://blimp.io/" rel="nofollow" style="background-image:url(../img/sponsors/3-blimp.png);">Blimp</a></li>
|
||||
<li><a href="http://pathwright.com" rel="nofollow" style="background-image:url(../img/sponsors/3-pathwright.png);">Pathwright</a></li>
|
||||
<li><a href="http://fluxility.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-fluxility.png);">Fluxility</a></li>
|
||||
<li><a href="http://teonite.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-teonite.png);">Teonite</a></li>
|
||||
<li><a href="http://trackmaven.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-trackmaven.png);">TrackMaven</a></li>
|
||||
<li><a href="http://www.phurba.net/" rel="nofollow" style="background-image:url(../img/sponsors/3-phurba.png);">Phurba</a></li>
|
||||
<li><a href="http://www.nephila.co.uk/" rel="nofollow" style="background-image:url(../img/sponsors/3-nephila.png);">Nephila</a></li>
|
||||
<li><a href="http://www.aditium.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-aditium.png);">Aditium</a></li>
|
||||
<li><a href="http://www.eyesopen.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-openeye.png);">OpenEye Scientific Software</a></li>
|
||||
<li><a href="https://holvi.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-holvi.png);">Holvi</a></li>
|
||||
<li><a href="http://cantemo.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-cantemo.gif);">Cantemo</a></li>
|
||||
<li><a href="https://www.makespace.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-makespace.png);">MakeSpace</a></li>
|
||||
<li><a href="https://www.ax-semantics.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-ax_semantics.png);">AX Semantics</a></li>
|
||||
<li><a href="http://istrategylabs.com/" rel="nofollow" style="background-image:url(../img/sponsors/3-isl.png);">ISL</a></li>
|
||||
</ul>
|
||||
|
||||
<div style="clear: both; padding-bottom: 40px;"></div>
|
||||
|
||||
**Individual backers**: Paul Hallet, <a href="http://www.paulwhippconsulting.com/">Paul Whipp</a>, Dylan Roy, Jannis Leidel, <a href="https://linovia.com/en/">Xavier Ordoquy</a>, <a href="http://spielmannsolutions.com/">Johannes Spielmann</a>, <a href="http://brooklynhacker.com/">Rob Spectre</a>, <a href="http://chrisheisel.com/">Chris Heisel</a>, Marwan Alsabbagh, Haris Ali, Tuomas Toivonen.
|
||||
|
||||
---
|
||||
|
||||
### Advocates
|
||||
|
||||
The following individuals made a significant financial contribution to the development of Django REST framework 3, for which I can only offer a huge, warm and sincere thank you!
|
||||
|
||||
**Individual backers**: Jure Cuhalev, Kevin Brolly, Ferenc Szalai, Dougal Matthews, Stefan Foulis, Carlos Hernando, Alen Mujezinovic, Ross Crawford-d'Heureuse, George Kappel, Alasdair Nicol, John Carr, Steve Winton, Trey, Manuel Miranda, David Horn, Vince Mi, Daniel Sears, Jamie Matthews, Ryan Currah, Marty Kemka, Scott Nixon, Moshin Elahi, Kevin Campbell, Jose Antonio Leiva Izquierdo, Kevin Stone, Andrew Godwin, Tijs Teulings, Roger Boardman, Xavier Antoviaque, Darian Moody, Lujeni, Jon Dugan, Wiley Kestner, Daniel C. Silverstein, Daniel Hahler, Subodh Nijsure, Philipp Weidenhiller, Yusuke Muraoka, Danny Roa, Reto Aebersold, Kyle Getrost, Décébal Hormuz, James Dacosta, Matt Long, Mauro Rocco, Tyrel Souza, Ryan Campbell, Ville Jyrkkä, Charalampos Papaloizou, Nikolai Røed Kristiansen, Antoni Aloy López, Celia Oakley, Michał Krawczak, Ivan VenOsdel, Tim Watts, Martin Warne, Nicola Jordan, Ryan Kaskel.
|
||||
|
||||
**Corporate backers**: Savannah Informatics, Prism Skylabs, Musical Operating Devices.
|
||||
|
||||
---
|
||||
|
||||
### Supporters
|
||||
|
||||
There were also almost 300 further individuals choosing to help fund the project at other levels or choosing to give anonymously. Again, thank you, thank you, thank you!
|
|
@ -129,7 +129,7 @@ Then, add the following property to **both** the `SnippetList` and `SnippetDetai
|
|||
|
||||
If you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.
|
||||
|
||||
We can add a login view for use with the browsable API, by editing the URLconf in our project-level urls.py file.
|
||||
We can add a login view for use with the browsable API, by editing the URLconf in our project-level `urls.py` file.
|
||||
|
||||
Add the following import at the top of the file:
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ At the moment relationships within our API are represented by using primary keys
|
|||
|
||||
## Creating an endpoint for the root of our API
|
||||
|
||||
Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the `@api_view` decorator we introduced earlier.
|
||||
Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the `@api_view` decorator we introduced earlier. In your `snippets/views.py` add:
|
||||
|
||||
from rest_framework import renderers
|
||||
from rest_framework.decorators import api_view
|
||||
|
@ -29,7 +29,7 @@ Unlike all our other API endpoints, we don't want to use JSON, but instead just
|
|||
|
||||
The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.
|
||||
|
||||
Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method. In your snippets.views add:
|
||||
Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method. In your `snippets/views.py` add:
|
||||
|
||||
from rest_framework import renderers
|
||||
from rest_framework.response import Response
|
||||
|
@ -43,7 +43,7 @@ Instead of using a concrete generic view, we'll use the base class for represent
|
|||
return Response(snippet.highlighted)
|
||||
|
||||
As usual we need to add the new views that we've created in to our URLconf.
|
||||
We'll add a url pattern for our new API root:
|
||||
We'll add a url pattern for our new API root in `snippets/urls.py`:
|
||||
|
||||
url(r'^$', 'api_root'),
|
||||
|
||||
|
@ -73,7 +73,7 @@ The `HyperlinkedModelSerializer` has the following differences from `ModelSerial
|
|||
* Relationships use `HyperlinkedRelatedField`,
|
||||
instead of `PrimaryKeyRelatedField`.
|
||||
|
||||
We can easily re-write our existing serializers to use hyperlinking.
|
||||
We can easily re-write our existing serializers to use hyperlinking. In your `snippets/serializers.py` add:
|
||||
|
||||
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
|
||||
owner = serializers.Field(source='owner.username')
|
||||
|
@ -105,7 +105,7 @@ If we're going to have a hyperlinked API, we need to make sure we name our URL p
|
|||
* Our user serializer includes a field that refers to `'snippet-detail'`.
|
||||
* Our snippet and user serializers include `'url'` fields that by default will refer to `'{model_name}-detail'`, which in this case will be `'snippet-detail'` and `'user-detail'`.
|
||||
|
||||
After adding all those names into our URLconf, our final `'urls.py'` file should look something like this:
|
||||
After adding all those names into our URLconf, our final `snippets/urls.py` file should look something like this:
|
||||
|
||||
# API endpoints
|
||||
urlpatterns = format_suffix_patterns(patterns('snippets.views',
|
||||
|
|
|
@ -6,8 +6,8 @@ We're going to create a simple API to allow admin users to view and edit the use
|
|||
|
||||
Create a new Django project named `tutorial`, then start a new app called `quickstart`.
|
||||
|
||||
# Set up a new project
|
||||
django-admin.py startproject tutorial
|
||||
# Create the project directory
|
||||
mkdir tutorial
|
||||
cd tutorial
|
||||
|
||||
# Create a virtualenv to isolate our package dependencies locally
|
||||
|
@ -18,6 +18,9 @@ Create a new Django project named `tutorial`, then start a new app called `quick
|
|||
pip install django
|
||||
pip install djangorestframework
|
||||
|
||||
# Set up a new project
|
||||
django-admin.py startproject tutorial
|
||||
|
||||
# Create a new app
|
||||
python manage.py startapp quickstart
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
|
|||
toc += template + '\n'
|
||||
|
||||
if filename == 'index.md':
|
||||
main_title = 'Django REST framework - APIs made easy'
|
||||
main_title = 'Django REST framework - Web APIs for Django'
|
||||
else:
|
||||
main_title = main_title + ' - Django REST framework'
|
||||
|
||||
|
|
|
@ -310,6 +310,13 @@ class OAuth2Authentication(BaseAuthentication):
|
|||
|
||||
auth = get_authorization_header(request).split()
|
||||
|
||||
if len(auth) == 1:
|
||||
msg = 'Invalid bearer header. No credentials provided.'
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
elif len(auth) > 2:
|
||||
msg = 'Invalid bearer header. Token string should not contain spaces.'
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
if auth and auth[0].lower() == b'bearer':
|
||||
access_token = auth[1]
|
||||
elif 'access_token' in request.POST:
|
||||
|
@ -319,13 +326,6 @@ class OAuth2Authentication(BaseAuthentication):
|
|||
else:
|
||||
return None
|
||||
|
||||
if len(auth) == 1:
|
||||
msg = 'Invalid bearer header. No credentials provided.'
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
elif len(auth) > 2:
|
||||
msg = 'Invalid bearer header. Token string should not contain spaces.'
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
return self.authenticate_credentials(request, access_token)
|
||||
|
||||
def authenticate_credentials(self, request, access_token):
|
||||
|
|
|
@ -44,12 +44,15 @@ except ImportError:
|
|||
django_filters = None
|
||||
|
||||
|
||||
# django-guardian is optional
|
||||
# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
|
||||
# Fixes (#1712). We keep the try/except for the test suite.
|
||||
guardian = None
|
||||
if 'guardian' in settings.INSTALLED_APPS:
|
||||
try:
|
||||
import guardian
|
||||
import guardian.shortcuts # Fixes #1624
|
||||
except ImportError:
|
||||
guardian = None
|
||||
pass
|
||||
|
||||
|
||||
# cStringIO only if it's available, otherwise StringIO
|
||||
|
|
|
@ -116,6 +116,10 @@ class OrderingFilter(BaseFilterBackend):
|
|||
def get_ordering(self, request):
|
||||
"""
|
||||
Ordering is set by a comma delimited ?ordering=... query parameter.
|
||||
|
||||
The `ordering` query parameter can be overridden by setting
|
||||
the `ordering_param` value on the OrderingFilter or by
|
||||
specifying an `ORDERING_PARAM` value in the API settings.
|
||||
"""
|
||||
params = request.QUERY_PARAMS.get(self.ordering_param)
|
||||
if params:
|
||||
|
|
|
@ -43,6 +43,10 @@ class GenericAPIView(views.APIView):
|
|||
|
||||
# You'll need to either set these attributes,
|
||||
# or override `get_queryset()`/`get_serializer_class()`.
|
||||
# If you are overriding a view method, it is important that you call
|
||||
# `get_queryset()` instead of accessing the `queryset` property directly,
|
||||
# as `queryset` will get evaluated only once, and those results are cached
|
||||
# for all subsequent requests.
|
||||
queryset = None
|
||||
serializer_class = None
|
||||
|
||||
|
@ -185,7 +189,13 @@ class GenericAPIView(views.APIView):
|
|||
"""
|
||||
Returns the list of filter backends that this view requires.
|
||||
"""
|
||||
filter_backends = self.filter_backends or []
|
||||
if self.filter_backends is None:
|
||||
filter_backends = []
|
||||
else:
|
||||
# Note that we are returning a *copy* of the class attribute,
|
||||
# so that it is safe for the view to mutate it if needed.
|
||||
filter_backends = list(self.filter_backends)
|
||||
|
||||
if not filter_backends and self.filter_backend:
|
||||
warnings.warn(
|
||||
'The `filter_backend` attribute and `FILTER_BACKEND` setting '
|
||||
|
@ -195,6 +205,7 @@ class GenericAPIView(views.APIView):
|
|||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
filter_backends = [self.filter_backend]
|
||||
|
||||
return filter_backends
|
||||
|
||||
|
||||
|
@ -258,6 +269,10 @@ class GenericAPIView(views.APIView):
|
|||
This must be an iterable, and may be a queryset.
|
||||
Defaults to using `self.queryset`.
|
||||
|
||||
This method should always be used rather than accessing `self.queryset`
|
||||
directly, as `self.queryset` gets evaluated only once, and those results
|
||||
are cached for all subsequent requests.
|
||||
|
||||
You may want to override this if you need to provide different
|
||||
querysets depending on the incoming request.
|
||||
|
||||
|
|
|
@ -54,32 +54,37 @@ class JSONRenderer(BaseRenderer):
|
|||
format = 'json'
|
||||
encoder_class = encoders.JSONEncoder
|
||||
ensure_ascii = True
|
||||
charset = None
|
||||
# JSON is a binary encoding, that can be encoded as utf-8, utf-16 or utf-32.
|
||||
|
||||
# We don't set a charset because JSON is a binary encoding,
|
||||
# that can be encoded as utf-8, utf-16 or utf-32.
|
||||
# See: http://www.ietf.org/rfc/rfc4627.txt
|
||||
# Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/
|
||||
charset = None
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
"""
|
||||
Render `data` into JSON.
|
||||
"""
|
||||
if data is None:
|
||||
return bytes()
|
||||
|
||||
# If 'indent' is provided in the context, then pretty print the result.
|
||||
# E.g. If we're being called by the BrowsableAPIRenderer.
|
||||
renderer_context = renderer_context or {}
|
||||
indent = renderer_context.get('indent', None)
|
||||
|
||||
def get_indent(self, accepted_media_type, renderer_context):
|
||||
if accepted_media_type:
|
||||
# If the media type looks like 'application/json; indent=4',
|
||||
# then pretty print the result.
|
||||
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
|
||||
indent = params.get('indent', indent)
|
||||
try:
|
||||
indent = max(min(int(indent), 8), 0)
|
||||
except (ValueError, TypeError):
|
||||
indent = None
|
||||
return max(min(int(params['indent']), 8), 0)
|
||||
except (KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# If 'indent' is provided in the context, then pretty print the result.
|
||||
# E.g. If we're being called by the BrowsableAPIRenderer.
|
||||
return renderer_context.get('indent', None)
|
||||
|
||||
|
||||
def render(self, data, accepted_media_type=None, renderer_context=None):
|
||||
"""
|
||||
Render `data` into JSON, returning a bytestring.
|
||||
"""
|
||||
if data is None:
|
||||
return bytes()
|
||||
|
||||
renderer_context = renderer_context or {}
|
||||
indent = self.get_indent(accepted_media_type, renderer_context)
|
||||
|
||||
ret = json.dumps(data, cls=self.encoder_class,
|
||||
indent=indent, ensure_ascii=self.ensure_ascii)
|
||||
|
|
|
@ -42,13 +42,20 @@ class override_method(object):
|
|||
self.view = view
|
||||
self.request = request
|
||||
self.method = method
|
||||
self.action = getattr(view, 'action', None)
|
||||
|
||||
def __enter__(self):
|
||||
self.view.request = clone_request(self.request, self.method)
|
||||
if self.action is not None:
|
||||
# For viewsets we also set the `.action` attribute.
|
||||
action_map = getattr(self.view, 'action_map', {})
|
||||
self.view.action = action_map.get(self.method.lower())
|
||||
return self.view.request
|
||||
|
||||
def __exit__(self, *args, **kwarg):
|
||||
self.view.request = self.request
|
||||
if self.action is not None:
|
||||
self.view.action = self.action
|
||||
|
||||
|
||||
class Empty(object):
|
||||
|
@ -280,8 +287,8 @@ class Request(object):
|
|||
self._method = self._request.method
|
||||
|
||||
# Allow X-HTTP-METHOD-OVERRIDE header
|
||||
self._method = self.META.get('HTTP_X_HTTP_METHOD_OVERRIDE',
|
||||
self._method)
|
||||
if 'HTTP_X_HTTP_METHOD_OVERRIDE' in self.META:
|
||||
self._method = self.META['HTTP_X_HTTP_METHOD_OVERRIDE'].upper()
|
||||
|
||||
def _load_stream(self):
|
||||
"""
|
||||
|
|
|
@ -5,6 +5,7 @@ it is initialized with unrendered data, instead of a pre-rendered string.
|
|||
The appropriate renderer is called during Django's template response rendering.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
import django
|
||||
from django.core.handlers.wsgi import STATUS_CODE_TEXT
|
||||
from django.template.response import SimpleTemplateResponse
|
||||
from rest_framework.compat import six
|
||||
|
@ -15,8 +16,11 @@ class Response(SimpleTemplateResponse):
|
|||
An HttpResponse that allows its data to be rendered into
|
||||
arbitrary media types.
|
||||
"""
|
||||
# TODO: remove that once Django 1.3 isn't supported
|
||||
if django.VERSION >= (1, 4):
|
||||
rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_closable_objects']
|
||||
|
||||
def __init__(self, data=None, status=200,
|
||||
def __init__(self, data=None, status=None,
|
||||
template_name=None, headers=None,
|
||||
exception=False, content_type=None):
|
||||
"""
|
||||
|
|
|
@ -976,7 +976,7 @@ class ModelSerializer(Serializer):
|
|||
try:
|
||||
setattr(instance, key, val)
|
||||
except ValueError:
|
||||
self._errors[key] = self.error_messages['required']
|
||||
self._errors[key] = [self.error_messages['required']]
|
||||
|
||||
# Any relations that cannot be set until we've
|
||||
# saved the model get hidden away on these
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
{% endblock %}
|
||||
</head>
|
||||
|
||||
{% block body %}
|
||||
<body class="{% block bodyclass %}{% endblock %} container">
|
||||
|
||||
<div class="wrapper">
|
||||
|
@ -94,7 +95,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if options_form %}
|
||||
<form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
|
||||
<form class="button-form" action="{{ request.get_full_path }}" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" />
|
||||
<button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
|
||||
|
@ -102,7 +103,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if delete_form %}
|
||||
<form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
|
||||
<form class="button-form" action="{{ request.get_full_path }}" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
|
||||
<button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button>
|
||||
|
@ -231,4 +232,5 @@
|
|||
<script src="{% static "rest_framework/js/default.js" %}"></script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
{% endblock %}
|
||||
</html>
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
{% extends "rest_framework/base.html" %}
|
||||
{% load url from future %}
|
||||
{% load staticfiles %}
|
||||
{% load rest_framework %}
|
||||
<html>
|
||||
|
||||
<head>
|
||||
{% block style %}
|
||||
{% block bootstrap_theme %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
{% block body %}
|
||||
<body class="container">
|
||||
|
||||
<div class="container-fluid" style="margin-top: 30px">
|
||||
|
@ -51,4 +42,4 @@
|
|||
</div><!-- /.row-fluid -->
|
||||
</div><!-- /.container-fluid -->
|
||||
</body>
|
||||
</html>
|
||||
{% endblock %}
|
||||
|
|
|
@ -154,6 +154,10 @@ class APIClient(APIRequestFactory, DjangoClient):
|
|||
kwargs.update(self._credentials)
|
||||
return super(APIClient, self).request(**kwargs)
|
||||
|
||||
def logout(self):
|
||||
self._credentials = {}
|
||||
return super(APIClient, self).logout()
|
||||
|
||||
|
||||
class APITransactionTestCase(testcases.TransactionTestCase):
|
||||
client_class = APIClient
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
Login and logout views for the browsable API.
|
||||
|
||||
Add these to your root URLconf if you're using the browsable API and
|
||||
your API requires authentication.
|
||||
|
||||
The urls must be namespaced as 'rest_framework', and you should make sure
|
||||
your authentication settings include `SessionAuthentication`.
|
||||
your API requires authentication:
|
||||
|
||||
urlpatterns = patterns('',
|
||||
...
|
||||
url(r'^auth', include('rest_framework.urls', namespace='rest_framework'))
|
||||
)
|
||||
|
||||
The urls must be namespaced as 'rest_framework', and you should make sure
|
||||
your authentication settings include `SessionAuthentication`.
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from django.conf.urls import patterns, url
|
||||
|
|
|
@ -550,6 +550,15 @@ class OAuth2Tests(TestCase):
|
|||
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||
def test_get_form_with_wrong_authorization_header_token_missing(self):
|
||||
"""Ensure that a missing token lead to the correct HTTP error status code"""
|
||||
auth = "Bearer"
|
||||
response = self.csrf_client.get('/oauth2-test/', {}, HTTP_AUTHORIZATION=auth)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
response = self.csrf_client.get('/oauth2-test/', HTTP_AUTHORIZATION=auth)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
@unittest.skipUnless(oauth2_provider, 'django-oauth2-provider not installed')
|
||||
def test_get_form_passing_auth(self):
|
||||
"""Ensure GETing form over OAuth with correct client credentials succeed"""
|
||||
|
|
|
@ -1002,3 +1002,21 @@ class BooleanField(TestCase):
|
|||
bool_field = serializers.BooleanField(required=True)
|
||||
|
||||
self.assertFalse(BooleanRequiredSerializer(data={}).is_valid())
|
||||
|
||||
|
||||
class SerializerMethodFieldTest(TestCase):
|
||||
"""
|
||||
Tests for the SerializerMethodField field_to_native() behavior
|
||||
"""
|
||||
class SerializerTest(serializers.Serializer):
|
||||
def get_my_test(self, obj):
|
||||
return obj.my_test[0:5]
|
||||
|
||||
class Example():
|
||||
my_test = 'Hey, this is a test !'
|
||||
|
||||
def test_field_to_native(self):
|
||||
s = serializers.SerializerMethodField('get_my_test')
|
||||
s.initialize(self.SerializerTest(), 'name')
|
||||
result = s.field_to_native(self.Example(), None)
|
||||
self.assertEqual(result, 'Hey, ')
|
||||
|
|
|
@ -686,7 +686,7 @@ class ModelValidationTests(TestCase):
|
|||
photo_serializer = PhotoSerializer(instance=photo, data={'album': ''}, partial=True)
|
||||
self.assertFalse(photo_serializer.is_valid())
|
||||
self.assertTrue('album' in photo_serializer.errors)
|
||||
self.assertEqual(photo_serializer.errors['album'], photo_serializer.error_messages['required'])
|
||||
self.assertEqual(photo_serializer.errors['album'], [photo_serializer.error_messages['required']])
|
||||
|
||||
def test_foreign_key_with_partial(self):
|
||||
"""
|
||||
|
|
|
@ -99,6 +99,17 @@ class TestAPITestClient(TestCase):
|
|||
self.assertEqual(response.status_code, 403)
|
||||
self.assertEqual(response.data, expected)
|
||||
|
||||
def test_can_logout(self):
|
||||
"""
|
||||
`logout()` reset stored credentials
|
||||
"""
|
||||
self.client.credentials(HTTP_AUTHORIZATION='example')
|
||||
response = self.client.get('/view/')
|
||||
self.assertEqual(response.data['auth'], 'example')
|
||||
self.client.logout()
|
||||
response = self.client.get('/view/')
|
||||
self.assertEqual(response.data['auth'], b'')
|
||||
|
||||
|
||||
class TestAPIRequestFactory(TestCase):
|
||||
def test_csrf_exempt_by_default(self):
|
||||
|
|