mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-06-19 04:53:20 +03:00
Merge branch 'master' into 675-empty-label-related-field
This commit is contained in:
commit
50ba26822d
|
@ -274,6 +274,8 @@ Exceptions raised and handled by an HTML renderer will attempt to render using o
|
||||||
|
|
||||||
Templates will render with a `RequestContext` which includes the `status_code` and `details` keys.
|
Templates will render with a `RequestContext` which includes the `status_code` and `details` keys.
|
||||||
|
|
||||||
|
**Note**: If `DEBUG=True`, Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Third party packages
|
# Third party packages
|
||||||
|
|
|
@ -35,6 +35,17 @@ A suitable replacement theme can be generated using Bootstrap's [Customize Tool]
|
||||||
|
|
||||||
You can also change the navbar variant, which by default is `navbar-inverse`, using the `bootstrap_navbar_variant` block. The empty `{% block bootstrap_navbar_variant %}{% endblock %}` will use the original Bootstrap navbar style.
|
You can also change the navbar variant, which by default is `navbar-inverse`, using the `bootstrap_navbar_variant` block. The empty `{% block bootstrap_navbar_variant %}{% endblock %}` will use the original Bootstrap navbar style.
|
||||||
|
|
||||||
|
Full Example
|
||||||
|
|
||||||
|
{% extends "rest_framework/base.html" %}
|
||||||
|
|
||||||
|
{% block bootstrap_theme %}
|
||||||
|
<link rel="stylesheet" href="/path/to/yourtheme/bootstrap.min.css' type="text/css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block bootstrap_navbar_variant %}{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
For more specific CSS tweaks, use the `style` block instead.
|
For more specific CSS tweaks, use the `style` block instead.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,8 @@ The following people have helped make REST framework great.
|
||||||
* Craig de Stigter - [craigds]
|
* Craig de Stigter - [craigds]
|
||||||
* Pablo Recio - [pyriku]
|
* Pablo Recio - [pyriku]
|
||||||
* Brian Zambrano - [brianz]
|
* Brian Zambrano - [brianz]
|
||||||
|
* Òscar Vilaplana - [grimborg]
|
||||||
|
* Ryan Kaskel - [ryankask]
|
||||||
|
|
||||||
Many thanks to everyone who's contributed to the project.
|
Many thanks to everyone who's contributed to the project.
|
||||||
|
|
||||||
|
@ -290,3 +292,5 @@ You can also contact [@_tomchristie][twitter] directly on twitter.
|
||||||
[craigds]: https://github.com/craigds
|
[craigds]: https://github.com/craigds
|
||||||
[pyriku]: https://github.com/pyriku
|
[pyriku]: https://github.com/pyriku
|
||||||
[brianz]: https://github.com/brianz
|
[brianz]: https://github.com/brianz
|
||||||
|
[grimborg]: https://github.com/grimborg
|
||||||
|
[ryankask]: https://github.com/ryankask
|
||||||
|
|
|
@ -378,23 +378,27 @@ class BaseSerializer(WritableField):
|
||||||
# Set the serializer object if it exists
|
# Set the serializer object if it exists
|
||||||
obj = getattr(self.parent.object, field_name) if self.parent.object else None
|
obj = getattr(self.parent.object, field_name) if self.parent.object else None
|
||||||
|
|
||||||
if value in (None, ''):
|
if self.source == '*':
|
||||||
into[(self.source or field_name)] = None
|
if value:
|
||||||
|
into.update(value)
|
||||||
else:
|
else:
|
||||||
kwargs = {
|
if value in (None, ''):
|
||||||
'instance': obj,
|
into[(self.source or field_name)] = None
|
||||||
'data': value,
|
|
||||||
'context': self.context,
|
|
||||||
'partial': self.partial,
|
|
||||||
'many': self.many
|
|
||||||
}
|
|
||||||
serializer = self.__class__(**kwargs)
|
|
||||||
|
|
||||||
if serializer.is_valid():
|
|
||||||
into[self.source or field_name] = serializer.object
|
|
||||||
else:
|
else:
|
||||||
# Propagate errors up to our parent
|
kwargs = {
|
||||||
raise NestedValidationError(serializer.errors)
|
'instance': obj,
|
||||||
|
'data': value,
|
||||||
|
'context': self.context,
|
||||||
|
'partial': self.partial,
|
||||||
|
'many': self.many
|
||||||
|
}
|
||||||
|
serializer = self.__class__(**kwargs)
|
||||||
|
|
||||||
|
if serializer.is_valid():
|
||||||
|
into[self.source or field_name] = serializer.object
|
||||||
|
else:
|
||||||
|
# Propagate errors up to our parent
|
||||||
|
raise NestedValidationError(serializer.errors)
|
||||||
|
|
||||||
def get_identity(self, data):
|
def get_identity(self, data):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,4 +19,163 @@ a single block in the template.
|
||||||
.navbar-inverse .brand:hover a {
|
.navbar-inverse .brand:hover a {
|
||||||
color: white;
|
color: white;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* custom navigation styles */
|
||||||
|
.wrapper .navbar{
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .navbar-inner{
|
||||||
|
background: #2C2C2C;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-top: 5px solid #A30000;
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand:hover{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-list > .active > a, .nav-list > .active > a:hover {
|
||||||
|
background: #2c2c2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li{
|
||||||
|
color: #A30000;
|
||||||
|
}
|
||||||
|
.navbar .navbar-inner .dropdown-menu li a:hover{
|
||||||
|
background: #eeeeee;
|
||||||
|
color: #c20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=== dabapps bootstrap styles ====*/
|
||||||
|
|
||||||
|
html{
|
||||||
|
width:100%;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, .navbar .navbar-inner .container-fluid {
|
||||||
|
max-width: 1150px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body{
|
||||||
|
background: url("../img/grid.png") repeat-x;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sticky footer and footer */
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
min-height: 100%;
|
||||||
|
height: auto !important;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-switcher {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.well {
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
-moz-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.well .form-actions {
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.well form {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > li {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs li a {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > .active > a {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tabs > .active > a:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbable.first-tab-active .tab-content
|
||||||
|
{
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer, #push {
|
||||||
|
height: 60px; /* .push must be the same height as .footer */
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer p {
|
||||||
|
text-align: center;
|
||||||
|
color: gray;
|
||||||
|
border-top: 1px solid #DDD;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer a {
|
||||||
|
color: gray;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer a:hover {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* custom general page styles */
|
||||||
|
.hero-unit h2, .hero-unit h1{
|
||||||
|
color: #A30000;
|
||||||
|
}
|
||||||
|
|
||||||
|
body a, body a{
|
||||||
|
color: #A30000;
|
||||||
|
}
|
||||||
|
|
||||||
|
body a:hover{
|
||||||
|
color: #c20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content a span{
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.request-info {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
|
@ -69,152 +69,3 @@ pre {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*=== dabapps bootstrap styles ====*/
|
|
||||||
|
|
||||||
html{
|
|
||||||
width:100%;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body, .navbar .navbar-inner .container-fluid {
|
|
||||||
max-width: 1150px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
body{
|
|
||||||
background: url("../img/grid.png") repeat-x;
|
|
||||||
background-attachment: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content{
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
/* custom navigation styles */
|
|
||||||
.wrapper .navbar{
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-inner{
|
|
||||||
background: #2C2C2C;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-top: 5px solid #A30000;
|
|
||||||
border-radius: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand{
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-list > .active > a, .nav-list > .active > a:hover {
|
|
||||||
background: #2c2c2c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li{
|
|
||||||
color: #A30000;
|
|
||||||
}
|
|
||||||
.navbar .navbar-inner .dropdown-menu li a:hover{
|
|
||||||
background: #eeeeee;
|
|
||||||
color: #c20000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* custom general page styles */
|
|
||||||
.hero-unit h2, .hero-unit h1{
|
|
||||||
color: #A30000;
|
|
||||||
}
|
|
||||||
|
|
||||||
body a, body a{
|
|
||||||
color: #A30000;
|
|
||||||
}
|
|
||||||
|
|
||||||
body a:hover{
|
|
||||||
color: #c20000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content a span{
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sticky footer and footer */
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.wrapper {
|
|
||||||
min-height: 100%;
|
|
||||||
height: auto !important;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 auto -60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-switcher {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well .form-actions {
|
|
||||||
padding-bottom: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well form {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs > li {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs li a {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs > .active > a {
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-tabs > .active > a:hover {
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tabbable.first-tab-active .tab-content
|
|
||||||
{
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer, #push {
|
|
||||||
height: 60px; /* .push must be the same height as .footer */
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer p {
|
|
||||||
text-align: center;
|
|
||||||
color: gray;
|
|
||||||
border-top: 1px solid #DDD;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer a {
|
|
||||||
color: gray;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer a:hover {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,10 @@
|
||||||
<title>{% block title %}Django REST framework{% endblock %}</title>
|
<title>{% block title %}Django REST framework{% endblock %}</title>
|
||||||
|
|
||||||
{% block style %}
|
{% block style %}
|
||||||
{% block bootstrap_theme %}<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>{% endblock %}
|
{% block bootstrap_theme %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
|
<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/prettify.css" %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -30,8 +32,8 @@
|
||||||
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
|
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<span class="brand" href="/">
|
<span href="/">
|
||||||
{% block branding %}<a href='http://django-rest-framework.org'>Django REST framework <span class="version">{{ version }}</span></a>{% endblock %}
|
{% block branding %}<a class='brand' href='http://django-rest-framework.org'>Django REST framework <span class="version">{{ version }}</span></a>{% endblock %}
|
||||||
</span>
|
</span>
|
||||||
<ul class="nav pull-right">
|
<ul class="nav pull-right">
|
||||||
{% block userlinks %}
|
{% block userlinks %}
|
||||||
|
@ -109,8 +111,7 @@
|
||||||
<div class="content-main">
|
<div class="content-main">
|
||||||
<div class="page-header"><h1>{{ name }}</h1></div>
|
<div class="page-header"><h1>{{ name }}</h1></div>
|
||||||
{{ description }}
|
{{ description }}
|
||||||
|
<div class="request-info" style="clear: both" >
|
||||||
<div class="request-info">
|
|
||||||
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
|
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="response-info">
|
<div class="response-info">
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% block style %}
|
{% block style %}
|
||||||
{% block bootstrap_theme %}<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>{% endblock %}
|
{% block bootstrap_theme %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
|
<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" %}"/>
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -91,6 +91,17 @@ class PersonSerializer(serializers.ModelSerializer):
|
||||||
read_only_fields = ('age',)
|
read_only_fields = ('age',)
|
||||||
|
|
||||||
|
|
||||||
|
class NestedSerializer(serializers.Serializer):
|
||||||
|
info = serializers.Field()
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSerializerWithNestedSerializer(serializers.ModelSerializer):
|
||||||
|
nested = NestedSerializer(source='*')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Person
|
||||||
|
|
||||||
|
|
||||||
class PersonSerializerInvalidReadOnly(serializers.ModelSerializer):
|
class PersonSerializerInvalidReadOnly(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Testing for #652.
|
Testing for #652.
|
||||||
|
@ -418,6 +429,17 @@ class ValidationTests(TestCase):
|
||||||
except:
|
except:
|
||||||
self.fail('Wrong exception type thrown.')
|
self.fail('Wrong exception type thrown.')
|
||||||
|
|
||||||
|
def test_writable_star_source_on_nested_serializer(self):
|
||||||
|
"""
|
||||||
|
Assert that a nested serializer instantiated with source='*' correctly
|
||||||
|
expands the data into the outer serializer.
|
||||||
|
"""
|
||||||
|
serializer = ModelSerializerWithNestedSerializer(data={
|
||||||
|
'name': 'marko',
|
||||||
|
'nested': {'info': 'hi'}},
|
||||||
|
)
|
||||||
|
self.assertEqual(serializer.is_valid(), True)
|
||||||
|
|
||||||
|
|
||||||
class CustomValidationTests(TestCase):
|
class CustomValidationTests(TestCase):
|
||||||
class CommentSerializerWithFieldValidator(CommentSerializer):
|
class CommentSerializerWithFieldValidator(CommentSerializer):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user