aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/api-guide/renderers.md2
-rw-r--r--docs/topics/browsable-api.md11
-rw-r--r--docs/topics/credits.md4
-rw-r--r--rest_framework/serializers.py34
-rw-r--r--rest_framework/static/rest_framework/css/bootstrap-tweaks.css161
-rw-r--r--rest_framework/static/rest_framework/css/default.css149
-rw-r--r--rest_framework/templates/rest_framework/base.html13
-rw-r--r--rest_framework/templates/rest_framework/login_base.html6
-rw-r--r--rest_framework/tests/serializer.py22
9 files changed, 229 insertions, 173 deletions
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
index ed733c65..b9a9fd7a 100644
--- a/docs/api-guide/renderers.md
+++ b/docs/api-guide/renderers.md
@@ -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.
+**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
diff --git a/docs/topics/browsable-api.md b/docs/topics/browsable-api.md
index 8ee01824..65f76abc 100644
--- a/docs/topics/browsable-api.md
+++ b/docs/topics/browsable-api.md
@@ -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.
+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.
diff --git a/docs/topics/credits.md b/docs/topics/credits.md
index 5998b4ca..bba78ba8 100644
--- a/docs/topics/credits.md
+++ b/docs/topics/credits.md
@@ -127,6 +127,8 @@ The following people have helped make REST framework great.
* Craig de Stigter - [craigds]
* Pablo Recio - [pyriku]
* Brian Zambrano - [brianz]
+* Òscar Vilaplana - [grimborg]
+* Ryan Kaskel - [ryankask]
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
[pyriku]: https://github.com/pyriku
[brianz]: https://github.com/brianz
+[grimborg]: https://github.com/grimborg
+[ryankask]: https://github.com/ryankask
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 500bb306..9cfeed37 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -378,23 +378,27 @@ class BaseSerializer(WritableField):
# Set the serializer object if it exists
obj = getattr(self.parent.object, field_name) if self.parent.object else None
- if value in (None, ''):
- into[(self.source or field_name)] = None
+ if self.source == '*':
+ if value:
+ into.update(value)
else:
- kwargs = {
- '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
+ if value in (None, ''):
+ into[(self.source or field_name)] = None
else:
- # Propagate errors up to our parent
- raise NestedValidationError(serializer.errors)
+ kwargs = {
+ '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):
"""
diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
index c650ef2e..9b520156 100644
--- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
+++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css
@@ -19,4 +19,163 @@ a single block in the template.
.navbar-inverse .brand:hover a {
color: white;
text-decoration: none;
-} \ No newline at end of file
+}
+
+/* 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;
+}
diff --git a/rest_framework/static/rest_framework/css/default.css b/rest_framework/static/rest_framework/css/default.css
index d806267b..0261a303 100644
--- a/rest_framework/static/rest_framework/css/default.css
+++ b/rest_framework/static/rest_framework/css/default.css
@@ -69,152 +69,3 @@ pre {
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;
-}
-
diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html
index 4410f285..9d939e73 100644
--- a/rest_framework/templates/rest_framework/base.html
+++ b/rest_framework/templates/rest_framework/base.html
@@ -13,8 +13,10 @@
<title>{% block title %}Django REST framework{% endblock %}</title>
{% block style %}
- {% block bootstrap_theme %}<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>{% endblock %}
- <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
+ {% 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/prettify.css" %}"/>
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
{% endblock %}
@@ -30,8 +32,8 @@
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
<div class="navbar-inner">
<div class="container-fluid">
- <span class="brand" href="/">
- {% block branding %}<a href='http://django-rest-framework.org'>Django REST framework <span class="version">{{ version }}</span></a>{% endblock %}
+ <span href="/">
+ {% block branding %}<a class='brand' href='http://django-rest-framework.org'>Django REST framework <span class="version">{{ version }}</span></a>{% endblock %}
</span>
<ul class="nav pull-right">
{% block userlinks %}
@@ -109,8 +111,7 @@
<div class="content-main">
<div class="page-header"><h1>{{ name }}</h1></div>
{{ description }}
-
- <div class="request-info">
+ <div class="request-info" style="clear: both" >
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
</div>
<div class="response-info">
diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html
index a3e73b6b..be9a0072 100644
--- a/rest_framework/templates/rest_framework/login_base.html
+++ b/rest_framework/templates/rest_framework/login_base.html
@@ -4,8 +4,10 @@
<head>
{% block style %}
- {% block bootstrap_theme %}<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>{% endblock %}
- <link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
+ {% 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>
diff --git a/rest_framework/tests/serializer.py b/rest_framework/tests/serializer.py
index 69b33209..50b06582 100644
--- a/rest_framework/tests/serializer.py
+++ b/rest_framework/tests/serializer.py
@@ -91,6 +91,17 @@ class PersonSerializer(serializers.ModelSerializer):
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):
"""
Testing for #652.
@@ -418,6 +429,17 @@ class ValidationTests(TestCase):
except:
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 CommentSerializerWithFieldValidator(CommentSerializer):