mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-14 05:36:50 +03:00
Merge remote-tracking branch 'origin/master' into 2.4.0
Conflicts: rest_framework/fields.py
This commit is contained in:
commit
4876bec9f5
11
README.md
11
README.md
|
@ -1,3 +1,13 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 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
|
# Django REST framework
|
||||||
|
|
||||||
[![build-status-image]][travis]
|
[![build-status-image]][travis]
|
||||||
|
@ -136,6 +146,7 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master
|
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master
|
||||||
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
|
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
|
||||||
[twitter]: https://twitter.com/_tomchristie
|
[twitter]: https://twitter.com/_tomchristie
|
||||||
|
|
|
@ -348,7 +348,7 @@ As an example, let's create a field that can be used represent the class name of
|
||||||
"""
|
"""
|
||||||
Serialize the object's class name.
|
Serialize the object's class name.
|
||||||
"""
|
"""
|
||||||
return obj.__class__
|
return obj.__class__.__name__
|
||||||
|
|
||||||
# Third party packages
|
# Third party packages
|
||||||
|
|
||||||
|
@ -358,9 +358,16 @@ The following third party packages are also available.
|
||||||
|
|
||||||
The [drf-compound-fields][drf-compound-fields] package provides "compound" serializer fields, such as lists of simple values, which can be described by other fields rather than serializers with the `many=True` option. Also provided are fields for typed dictionaries and values that can be either a specific type or a list of items of that type.
|
The [drf-compound-fields][drf-compound-fields] package provides "compound" serializer fields, such as lists of simple values, which can be described by other fields rather than serializers with the `many=True` option. Also provided are fields for typed dictionaries and values that can be either a specific type or a list of items of that type.
|
||||||
|
|
||||||
|
## DRF Extra Fields
|
||||||
|
|
||||||
|
The [drf-extra-fields][drf-extra-fields] package provides extra serializer fields for REST framework, including `Base64ImageField` and `PointField` classes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[cite]: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.cleaned_data
|
[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
|
[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
|
[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
|
[strftime]: http://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
|
||||||
[iso8601]: http://www.w3.org/TR/NOTE-datetime
|
[iso8601]: http://www.w3.org/TR/NOTE-datetime
|
||||||
[drf-compound-fields]: http://drf-compound-fields.readthedocs.org
|
[drf-compound-fields]: http://drf-compound-fields.readthedocs.org
|
||||||
|
[drf-extra-fields]: https://github.com/Hipo/drf-extra-fields
|
||||||
|
|
|
@ -199,8 +199,7 @@ This enables us to make queries like:
|
||||||
|
|
||||||
http://example.com/api/products?manufacturer__name=foo
|
http://example.com/api/products?manufacturer__name=foo
|
||||||
|
|
||||||
This is nice, but it shows underlying model structure in REST API, which may
|
This is nice, but it exposes the Django's double underscore convention as part of the API. If you instead want to explicitly name the filter argument you can instead explicitly include it on the `FilterSet` class:
|
||||||
be undesired, but you can use:
|
|
||||||
|
|
||||||
import django_filters
|
import django_filters
|
||||||
from myapp.models import Product
|
from myapp.models import Product
|
||||||
|
@ -208,7 +207,6 @@ be undesired, but you can use:
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
|
|
||||||
class ProductFilter(django_filters.FilterSet):
|
class ProductFilter(django_filters.FilterSet):
|
||||||
|
|
||||||
manufacturer = django_filters.CharFilter(name="manufacturer__name")
|
manufacturer = django_filters.CharFilter(name="manufacturer__name")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -36,6 +36,12 @@ For example:
|
||||||
self.check_object_permissions(self.request, obj)
|
self.check_object_permissions(self.request, obj)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
#### Limitations of object level permissions
|
||||||
|
|
||||||
|
For performance reasons the generic views will not automatically apply object level permissions to each instance in a queryset when returning a list of objects.
|
||||||
|
|
||||||
|
Often when you're using object level permissions you'll also want to [filter the queryset][filtering] appropriately, to ensure that users only have visibility onto instances that they are permitted to view.
|
||||||
|
|
||||||
## Setting the permission policy
|
## Setting the permission policy
|
||||||
|
|
||||||
The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example.
|
The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example.
|
||||||
|
@ -237,6 +243,7 @@ The [REST Condition][rest-condition] package is another extension for building c
|
||||||
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
|
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
|
||||||
[authentication]: authentication.md
|
[authentication]: authentication.md
|
||||||
[throttling]: throttling.md
|
[throttling]: throttling.md
|
||||||
|
[filtering]: filtering.md
|
||||||
[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions
|
[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions
|
||||||
[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
|
[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
|
||||||
[guardian]: https://github.com/lukaszb/django-guardian
|
[guardian]: https://github.com/lukaszb/django-guardian
|
||||||
|
|
|
@ -249,8 +249,17 @@ The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (an
|
||||||
|
|
||||||
app.router.register_model(MyModel)
|
app.router.register_model(MyModel)
|
||||||
|
|
||||||
|
## DRF-extensions
|
||||||
|
|
||||||
|
The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
|
||||||
|
|
||||||
[cite]: http://guides.rubyonrails.org/routing.html
|
[cite]: http://guides.rubyonrails.org/routing.html
|
||||||
[route-decorators]: viewsets.html#marking-extra-actions-for-routing
|
[route-decorators]: viewsets.html#marking-extra-actions-for-routing
|
||||||
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
|
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
|
||||||
[wq.db]: http://wq.io/wq.db
|
[wq.db]: http://wq.io/wq.db
|
||||||
[wq.db-router]: http://wq.io/docs/app.py
|
[wq.db-router]: http://wq.io/docs/app.py
|
||||||
|
[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/
|
||||||
|
[drf-extensions-routers]: http://chibisov.github.io/drf-extensions/docs/#routers
|
||||||
|
[drf-extensions-nested-viewsets]: http://chibisov.github.io/drf-extensions/docs/#nested-routes
|
||||||
|
[drf-extensions-collection-level-controllers]: http://chibisov.github.io/drf-extensions/docs/#collection-level-controllers
|
||||||
|
[drf-extensions-customizable-endpoint-names]: http://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
|
||||||
|
|
|
@ -9,6 +9,14 @@
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 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>
|
<p>
|
||||||
<h1 style="position: absolute;
|
<h1 style="position: absolute;
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
@ -201,6 +209,7 @@ General guides to using REST framework.
|
||||||
* [2.0 Announcement][rest-framework-2-announcement]
|
* [2.0 Announcement][rest-framework-2-announcement]
|
||||||
* [2.2 Announcement][2.2-announcement]
|
* [2.2 Announcement][2.2-announcement]
|
||||||
* [2.3 Announcement][2.3-announcement]
|
* [2.3 Announcement][2.3-announcement]
|
||||||
|
* [Kickstarter Announcement][kickstarter-announcement]
|
||||||
* [Release Notes][release-notes]
|
* [Release Notes][release-notes]
|
||||||
* [Credits][credits]
|
* [Credits][credits]
|
||||||
|
|
||||||
|
@ -315,6 +324,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[rest-framework-2-announcement]: topics/rest-framework-2-announcement.md
|
[rest-framework-2-announcement]: topics/rest-framework-2-announcement.md
|
||||||
[2.2-announcement]: topics/2.2-announcement.md
|
[2.2-announcement]: topics/2.2-announcement.md
|
||||||
[2.3-announcement]: topics/2.3-announcement.md
|
[2.3-announcement]: topics/2.3-announcement.md
|
||||||
|
[kickstarter-announcement]: topics/kickstarter-announcement.md
|
||||||
[release-notes]: topics/release-notes.md
|
[release-notes]: topics/release-notes.md
|
||||||
[credits]: topics/credits.md
|
[credits]: topics/credits.md
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,21 @@
|
||||||
})();
|
})();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
span.fusion-wrap a {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.fusion-poweredby {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
div.promo {display: none;}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body onload="prettyPrint()" class="{{ page_id }}-page">
|
<body onload="prettyPrint()" class="{{ page_id }}-page">
|
||||||
|
|
||||||
|
@ -106,6 +121,7 @@
|
||||||
<li><a href="{{ base_url }}/topics/rest-framework-2-announcement{{ suffix }}">2.0 Announcement</a></li>
|
<li><a href="{{ base_url }}/topics/rest-framework-2-announcement{{ suffix }}">2.0 Announcement</a></li>
|
||||||
<li><a href="{{ base_url }}/topics/2.2-announcement{{ suffix }}">2.2 Announcement</a></li>
|
<li><a href="{{ base_url }}/topics/2.2-announcement{{ suffix }}">2.2 Announcement</a></li>
|
||||||
<li><a href="{{ base_url }}/topics/2.3-announcement{{ suffix }}">2.3 Announcement</a></li>
|
<li><a href="{{ base_url }}/topics/2.3-announcement{{ suffix }}">2.3 Announcement</a></li>
|
||||||
|
<li><a href="{{ base_url }}/topics/kickstarter-announcement{{ suffix }}">Kickstarter Announcement</a></li>
|
||||||
<li><a href="{{ base_url }}/topics/release-notes{{ suffix }}">Release Notes</a></li>
|
<li><a href="{{ base_url }}/topics/release-notes{{ suffix }}">Release Notes</a></li>
|
||||||
<li><a href="{{ base_url }}/topics/credits{{ suffix }}">Credits</a></li>
|
<li><a href="{{ base_url }}/topics/credits{{ suffix }}">Credits</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -169,10 +185,8 @@
|
||||||
<div id="table-of-contents">
|
<div id="table-of-contents">
|
||||||
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
|
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
|
||||||
{{ toc }}
|
{{ toc }}
|
||||||
<div>
|
<div class="promo">
|
||||||
|
|
||||||
{{ ad_block }}
|
{{ ad_block }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -199,6 +213,7 @@
|
||||||
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
|
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
|
||||||
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
|
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
|
||||||
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
|
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
//$('.side-nav').scrollspy()
|
//$('.side-nav').scrollspy()
|
||||||
var shiftWindow = function() { scrollBy(0, -50) };
|
var shiftWindow = function() { scrollBy(0, -50) };
|
||||||
|
|
31
docs/topics/kickstarter-announcement.md
Normal file
31
docs/topics/kickstarter-announcement.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Kickstarting Django REST framework 3
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<iframe width="480" height="360" src="https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3/widget/video.html" frameborder="0" scrolling="no"> </iframe>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
In order to continue to drive the project forward, I'm launching a Kickstarter campaign to help fund the development of a major new release - Django REST framework 3.
|
||||||
|
|
||||||
|
## Project details
|
||||||
|
|
||||||
|
This new release will allow us to comprehensively address some of the shortcomings of the framework, and will aim to include the following:
|
||||||
|
|
||||||
|
* Faster, simpler and easier-to-use serializers.
|
||||||
|
* An alternative admin-style interface for the browsable API.
|
||||||
|
* Search and filtering controls made accessible in the browsable API.
|
||||||
|
* Alternative API pagination styles.
|
||||||
|
* Documentation around API versioning.
|
||||||
|
* Triage of outstanding tickets.
|
||||||
|
* Improving the ongoing quality and maintainability of the project.
|
||||||
|
|
||||||
|
Full details are available now on the [project page](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3).
|
||||||
|
|
||||||
|
If you're interested in helping make sustainable open source development a reality please [visit the Kickstarter page](https://www.kickstarter.com/projects/tomchristie/django-rest-framework-3) and consider funding the project.
|
||||||
|
|
||||||
|
I can't wait to see where this takes us!
|
||||||
|
|
||||||
|
Many thanks to everyone for your support so far,
|
||||||
|
|
||||||
|
Tom Christie :)
|
|
@ -162,8 +162,8 @@ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
|
||||||
output = output.replace('{{ canonical_url }}', canonical_url)
|
output = output.replace('{{ canonical_url }}', canonical_url)
|
||||||
|
|
||||||
if filename =='index.md':
|
if filename =='index.md':
|
||||||
output = output.replace('{{ ad_block }}', """<hr><p><strong>The team behind REST framework is launching a new API service.</strong></p>
|
output = output.replace('{{ ad_block }}', """<hr/>
|
||||||
<p>If you want to be first in line when we start issuing invitations, please <a href="http://brightapi.com">sign up here</a>.</p>""")
|
<script type="text/javascript" src="//cdn.fusionads.net/fusion.js?zoneid=1332&serve=C6SDP2Y&placement=djangorestframework" id="_fusionads_js"></script>""")
|
||||||
else:
|
else:
|
||||||
output = output.replace('{{ ad_block }}', '')
|
output = output.replace('{{ ad_block }}', '')
|
||||||
|
|
||||||
|
|
|
@ -189,7 +189,7 @@ class Field(object):
|
||||||
|
|
||||||
def field_to_native(self, obj, field_name):
|
def field_to_native(self, obj, field_name):
|
||||||
"""
|
"""
|
||||||
Given and object and a field name, returns the value that should be
|
Given an object and a field name, returns the value that should be
|
||||||
serialized for that field.
|
serialized for that field.
|
||||||
"""
|
"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
|
@ -470,10 +470,12 @@ class CharField(WritableField):
|
||||||
self.validators.append(validators.MaxLengthValidator(max_length))
|
self.validators.append(validators.MaxLengthValidator(max_length))
|
||||||
|
|
||||||
def from_native(self, value):
|
def from_native(self, value):
|
||||||
if value is None and not self.allow_none:
|
|
||||||
return ''
|
|
||||||
if isinstance(value, six.string_types):
|
if isinstance(value, six.string_types):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
if value is None and not self.allow_none:
|
||||||
|
return ''
|
||||||
|
|
||||||
return smart_text(value)
|
return smart_text(value)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,7 @@ class Album(RESTFrameworkModel):
|
||||||
title = models.CharField(max_length=100, unique=True)
|
title = models.CharField(max_length=100, unique=True)
|
||||||
ref = models.CharField(max_length=10, unique=True, null=True, blank=True)
|
ref = models.CharField(max_length=10, unique=True, null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
class Photo(RESTFrameworkModel):
|
class Photo(RESTFrameworkModel):
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
album = models.ForeignKey(Album)
|
album = models.ForeignKey(Album)
|
||||||
|
@ -112,7 +113,8 @@ class Photo(RESTFrameworkModel):
|
||||||
|
|
||||||
# Model for issue #324
|
# Model for issue #324
|
||||||
class BlankFieldModel(RESTFrameworkModel):
|
class BlankFieldModel(RESTFrameworkModel):
|
||||||
title = models.CharField(max_length=100, blank=True, null=False)
|
title = models.CharField(max_length=100, blank=True, null=False,
|
||||||
|
default="title")
|
||||||
|
|
||||||
|
|
||||||
# Model for issue #380
|
# Model for issue #380
|
||||||
|
|
|
@ -1237,6 +1237,9 @@ class BlankFieldTests(TestCase):
|
||||||
def test_create_model_null_field(self):
|
def test_create_model_null_field(self):
|
||||||
serializer = self.model_serializer_class(data={'title': None})
|
serializer = self.model_serializer_class(data={'title': None})
|
||||||
self.assertEqual(serializer.is_valid(), True)
|
self.assertEqual(serializer.is_valid(), True)
|
||||||
|
serializer.save()
|
||||||
|
self.assertIsNot(serializer.object.pk, None)
|
||||||
|
self.assertEqual(serializer.object.title, '')
|
||||||
|
|
||||||
def test_create_not_blank_field(self):
|
def test_create_not_blank_field(self):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user