Merge branch 'master' into localizedfloatfield

This commit is contained in:
kgeorgy 2017-03-31 16:14:17 +02:00 committed by GitHub
commit 479f3233ff
13 changed files with 116 additions and 84 deletions

View File

@ -356,6 +356,10 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into).
## drfpasswordless
[drfpasswordless][drfpasswordless] adds (Medium, Square Cash inspired) passwordless support to Django REST Framework's own TokenAuthentication scheme. Users log in and sign up with a token sent to a contact point like an email address or a mobile number.
[cite]: http://jacobian.org/writing/rest-worst-practices/
[http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
[http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
@ -396,3 +400,4 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[django-rest-auth]: https://github.com/Tivix/django-rest-auth
[django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2
[django-rest-knox]: https://github.com/James1345/django-rest-knox
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless

View File

@ -142,7 +142,7 @@ Note that you can use both an overridden `.get_queryset()` and generic filtering
The `django-filter` library includes a `DjangoFilterBackend` class which
supports highly customizable field filtering for REST framework.
To use `DjangoFilterBackend`, first install `django-filter`.
To use `DjangoFilterBackend`, first install `django-filter`. Then add `django_filters` to Django's `INSTALLED_APPS`
pip install django-filter

View File

@ -140,10 +140,14 @@ Sign up for a paid plan today, and help ensure that REST framework becomes a sus
>
> — José Padilla, Django REST framework contributor
 
> The number one feature of the Python programming language is its community. Such a community is only possible because of the Open Source nature of the language and all the culture that comes from it. Building great Open Source projects require great minds. Given that, we at Vinta are not only proud to sponsor the team behind DRF but we also recognize the ROI that comes from it.
>
> — Filipe Ximenes, Vinta Software
 
> It's really awesome that this project continues to endure. The code base is top notch and the maintainers are committed to the highest level of quality.
DRF is one of the core reasons why Django is top choice among web frameworks today. In my opinion, it sets the standard for rest frameworks for the development community at large.
>

View File

@ -190,6 +190,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [djoser][djoser] - Provides a set of views to handle basic actions such as registration, login, logout, password reset and account activation.
* [django-rest-auth][django-rest-auth] - Provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc.
* [drf-oidc-auth][drf-oidc-auth] - Implements OpenID Connect token authentication for DRF.
* [drfpasswordless][drfpasswordless] - Adds (Medium, Square Cash inspired) passwordless logins and signups via email and mobile numbers.
### Permissions
@ -330,3 +331,4 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[drf-oidc-auth]: https://github.com/ByteInternet/drf-oidc-auth
[drf-serializer-extensions]: https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions
[djangorestframework-queryfields]: https://github.com/wimglenn/djangorestframework-queryfields
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless

View File

@ -41,7 +41,7 @@ view in our URL configuration.
schema_view = get_schema_view(title='Pastebin API')
urlpatterns = [
       url(r'^schema/$', schema_view),
   url(r'^schema/$', schema_view),
...
]

View File

@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "main.html" %}
{% block content %}

View File

@ -4,11 +4,11 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>{% if page_title %}{{ page_title }} - {% endif %}{{ site_name }}</title>
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
<link href="{{ base_url }}/img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="canonical" href="{{ canonical_url }}" />
<link rel="canonical" href="{{ page.canonical_url }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST{% if current_page %}, {{ current_page.title }}{% endif %}">
<meta name="description" content="Django, API, REST{% if page %}, {{ page.title }}{% endif %}">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
@ -51,7 +51,7 @@
}
</style>
</head>
<body onload="prettyPrint()" class="{% if current_page and current_page.is_homepage %}index{% endif %}-page">
<body onload="prettyPrint()" class="{% if page and page.is_homepage %}index{% endif %}-page">
<div class="wrapper">
{% include "nav.html" %}
@ -83,14 +83,14 @@
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
{% if current_page and current_page.is_homepage %}
{% if page and page.is_homepage %}
<li class="main">
<a href="#">Django REST framework</a>
</li>
{% endif %}
{% for toc_item in toc %}
<li class="{% if current_page and not current_page.is_homepage %}main{% endif %}">
{% for toc_item in page.toc %}
<li class="{% if page and not page.is_homepage %}main{% endif %}">
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
</li>
@ -112,15 +112,15 @@
<div id="main-content" class="span9">
{% block content %}
{% if meta.source %}
{% for filename in meta.source %}
{% if page.meta.source %}
{% for filename in page.meta.source %}
<a class="github" href="https://github.com/tomchristie/django-rest-framework/tree/master/rest_framework/{{ filename }}">
<span class="label label-info">{{ filename }}</span>
</a>
{% endfor %}
{% endif %}
{{ content }}
{{ page.content }}
{% endblock %}
</div> <!--/span-->

View File

@ -2,10 +2,10 @@
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small {% if not next_page %}disabled{% endif %}" rel="prev" {% if next_page %}href="{{ next_page.url }}"{% endif %}>
<a class="repo-link btn btn-inverse btn-small {% if not page.next_page %}disabled{% endif %}" rel="prev" {% if page.next_page %}href="{{ page.next_page.url }}"{% endif %}>
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small {% if not previous_page %}disabled{% endif %}" rel="next" {% if previous_page %}href="{{ previous_page.url }}"{% endif %}>
<a class="repo-link btn btn-inverse btn-small {% if not page.previous_page %}disabled{% endif %}" rel="next" {% if page.previous_page %}href="{{ page.previous_page.url }}"{% endif %}>
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
@ -16,7 +16,7 @@
</a>
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
<div class="nav-collapse collapse">
{% if include_nav %}
{% if nav|length>1 %}
<!-- Main navigation -->
<ul class="nav navbar-nav">
{% for nav_item in nav %} {% if nav_item.children %}

View File

@ -1,2 +1,2 @@
# MkDocs to build our documentation.
mkdocs==0.15.3
mkdocs==0.16.2

View File

@ -651,6 +651,7 @@ class BooleanField(Field):
initial = False
TRUE_VALUES = {
't', 'T',
'y', 'Y', 'yes', 'YES',
'true', 'True', 'TRUE',
'on', 'On', 'ON',
'1', 1,
@ -658,6 +659,7 @@ class BooleanField(Field):
}
FALSE_VALUES = {
'f', 'F',
'n', 'N', 'no', 'NO',
'false', 'False', 'FALSE',
'off', 'Off', 'OFF',
'0', 0, 0.0,

View File

@ -9,7 +9,8 @@ REST_FRAMEWORK = {
)
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.TemplateHTMLRenderer',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
}

View File

@ -123,43 +123,8 @@ def get_field_kwargs(field_name, model_field):
kwargs['allow_folders'] = model_field.allow_folders
if model_field.choices:
# If this model field contains choices, then return early.
# Further keyword arguments are not valid.
kwargs['choices'] = model_field.choices
return kwargs
# Our decimal validation is handled in the field code, not validator code.
# (In Django 1.9+ this differs from previous style)
if isinstance(model_field, models.DecimalField) and DecimalValidator:
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, DecimalValidator)
]
# Ensure that max_length is passed explicitly as a keyword arg,
# rather than as a validator.
max_length = getattr(model_field, 'max_length', None)
if max_length is not None and (isinstance(model_field, models.CharField) or
isinstance(model_field, models.TextField)):
kwargs['max_length'] = max_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxLengthValidator)
]
# Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator.
min_length = next((
validator.limit_value for validator in validator_kwarg
if isinstance(validator, validators.MinLengthValidator)
), None)
if min_length is not None and isinstance(model_field, models.CharField):
kwargs['min_length'] = min_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MinLengthValidator)
]
else:
# Ensure that max_value is passed explicitly as a keyword arg,
# rather than as a validator.
max_value = next((
@ -215,6 +180,37 @@ def get_field_kwargs(field_name, model_field):
validator for validator in validator_kwarg
if validator is not validators.validate_ipv46_address
]
# Our decimal validation is handled in the field code, not validator code.
# (In Django 1.9+ this differs from previous style)
if isinstance(model_field, models.DecimalField) and DecimalValidator:
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, DecimalValidator)
]
# Ensure that max_length is passed explicitly as a keyword arg,
# rather than as a validator.
max_length = getattr(model_field, 'max_length', None)
if max_length is not None and (isinstance(model_field, models.CharField) or
isinstance(model_field, models.TextField)):
kwargs['max_length'] = max_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MaxLengthValidator)
]
# Ensure that min_length is passed explicitly as a keyword arg,
# rather than as a validator.
min_length = next((
validator.limit_value for validator in validator_kwarg
if isinstance(validator, validators.MinLengthValidator)
), None)
if min_length is not None and isinstance(model_field, models.CharField):
kwargs['min_length'] = min_length
validator_kwarg = [
validator for validator in validator_kwarg
if not isinstance(validator, validators.MinLengthValidator)
]
if getattr(model_field, 'unique', False):
unique_error_message = model_field.error_messages.get('unique', None)

View File

@ -99,6 +99,15 @@ class Issue3674ChildModel(models.Model):
value = models.CharField(primary_key=True, max_length=64)
class UniqueChoiceModel(models.Model):
CHOICES = (
('choice1', 'choice 1'),
('choice2', 'choice 1'),
)
name = models.CharField(max_length=254, unique=True, choices=CHOICES)
class TestModelSerializer(TestCase):
def test_create_method(self):
class TestSerializer(serializers.ModelSerializer):
@ -1080,3 +1089,16 @@ class Issue4897TestCase(TestCase):
with pytest.raises(AssertionError) as cm:
TestSerializer(obj).fields
cm.match(r'readonly_fields')
class Test5004UniqueChoiceField(TestCase):
def test_unique_choice_field(self):
class TestUniqueChoiceSerializer(serializers.ModelSerializer):
class Meta:
model = UniqueChoiceModel
fields = '__all__'
UniqueChoiceModel.objects.create(name='choice1')
serializer = TestUniqueChoiceSerializer(data={'name': 'choice1'})
assert not serializer.is_valid()
assert serializer.errors == {'name': ['unique choice model with this name already exists.']}