Merge and styling fixes

This commit is contained in:
Tom Christie 2013-05-21 12:01:56 +01:00
commit cdc3c37465
8 changed files with 116 additions and 10 deletions

View File

@ -109,7 +109,7 @@ class Field(object):
use_files = False use_files = False
form_field_class = forms.CharField form_field_class = forms.CharField
def __init__(self, source=None): def __init__(self, source=None, label=None, help_text=None):
self.parent = None self.parent = None
self.creation_counter = Field.creation_counter self.creation_counter = Field.creation_counter
@ -117,6 +117,12 @@ class Field(object):
self.source = source self.source = source
if label is not None:
self.label = smart_text(label)
if help_text is not None:
self.help_text = smart_text(help_text)
def initialize(self, parent, field_name): def initialize(self, parent, field_name):
""" """
Called to set up a field prior to field_to_native or field_from_native. Called to set up a field prior to field_to_native or field_from_native.
@ -200,7 +206,8 @@ class WritableField(Field):
widget = widgets.TextInput widget = widgets.TextInput
default = None default = None
def __init__(self, source=None, read_only=False, required=None, def __init__(self, source=None, label=None, help_text=None,
read_only=False, required=None,
validators=[], error_messages=None, widget=None, validators=[], error_messages=None, widget=None,
default=None, blank=None): default=None, blank=None):
@ -211,7 +218,7 @@ class WritableField(Field):
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
required = not(blank) required = not(blank)
super(WritableField, self).__init__(source=source) super(WritableField, self).__init__(source=source, label=label, help_text=help_text)
self.read_only = read_only self.read_only = read_only
if required is None: if required is None:

View File

@ -385,7 +385,11 @@ class BrowsableAPIRenderer(BaseRenderer):
if getattr(v, 'default', None) is not None: if getattr(v, 'default', None) is not None:
kwargs['initial'] = v.default kwargs['initial'] = v.default
kwargs['label'] = k if getattr(v, 'label', None) is not None:
kwargs['label'] = v.label
if getattr(v, 'help_text', None) is not None:
kwargs['help_text'] = v.help_text
fields[k] = v.form_field_class(**kwargs) fields[k] = v.form_field_class(**kwargs)

View File

@ -739,6 +739,12 @@ class ModelSerializer(Serializer):
if issubclass(model_field.__class__, models.TextField): if issubclass(model_field.__class__, models.TextField):
kwargs['widget'] = widgets.Textarea kwargs['widget'] = widgets.Textarea
if model_field.verbose_name is not None:
kwargs['label'] = model_field.verbose_name
if model_field.help_text is not None:
kwargs['help_text'] = model_field.help_text
# TODO: TypedChoiceField? # TODO: TypedChoiceField?
if model_field.flatchoices: # This ModelField contains choices if model_field.flatchoices: # This ModelField contains choices
kwargs['choices'] = model_field.flatchoices kwargs['choices'] = model_field.flatchoices

View File

@ -104,6 +104,10 @@ html, body {
margin-bottom: 0; margin-bottom: 0;
} }
.well form .help-block {
color: #999;
}
.nav-tabs { .nav-tabs {
border: 0; border: 0;
} }

View File

@ -6,7 +6,7 @@
{{ field.label_tag|add_class:"control-label" }} {{ field.label_tag|add_class:"control-label" }}
<div class="controls"> <div class="controls">
{{ field }} {{ field }}
<span class="help-inline">{{ field.help_text }}</span> <span class="help-block">{{ field.help_text }}</span>
<!--{{ field.errors|add_class:"help-block" }}--> <!--{{ field.errors|add_class:"help-block" }}-->
</div> </div>
</div> </div>

View File

@ -1,5 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
def foobar(): def foobar():
@ -32,7 +34,7 @@ class Anchor(RESTFrameworkModel):
class BasicModel(RESTFrameworkModel): class BasicModel(RESTFrameworkModel):
text = models.CharField(max_length=100) text = models.CharField(max_length=100, verbose_name=_("Text comes here"), help_text=_("Text description."))
class SlugBasedModel(RESTFrameworkModel): class SlugBasedModel(RESTFrameworkModel):
@ -159,3 +161,9 @@ class NullableOneToOneSource(RESTFrameworkModel):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
target = models.OneToOneField(OneToOneTarget, null=True, blank=True, target = models.OneToOneField(OneToOneTarget, null=True, blank=True,
related_name='nullable_source') related_name='nullable_source')
# Serializer used to test BasicModel
class BasicModelSerializer(serializers.ModelSerializer):
class Meta:
model = BasicModel

View File

@ -1,14 +1,18 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from rest_framework.tests.models import BasicModel, BasicModelSerializer
from rest_framework.compat import patterns, url, include from rest_framework.compat import patterns, url, include
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework import generics
from rest_framework import routers
from rest_framework import status from rest_framework import status
from rest_framework.renderers import ( from rest_framework.renderers import (
BaseRenderer, BaseRenderer,
JSONRenderer, JSONRenderer,
BrowsableAPIRenderer BrowsableAPIRenderer
) )
from rest_framework import viewsets
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.compat import six from rest_framework.compat import six
@ -80,12 +84,30 @@ class HTMLView1(APIView):
def get(self, request, **kwargs): def get(self, request, **kwargs):
return Response('text') return Response('text')
class HTMLNewModelViewSet(viewsets.ModelViewSet):
model = BasicModel
class HTMLNewModelView(generics.ListCreateAPIView):
renderer_classes = (BrowsableAPIRenderer,)
permission_classes = []
serializer_class = BasicModelSerializer
model = BasicModel
new_model_viewset_router = routers.DefaultRouter()
new_model_viewset_router.register(r'', HTMLNewModelViewSet)
urlpatterns = patterns('', urlpatterns = patterns('',
url(r'^setbyview$', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^setbyview$', MockViewSettingContentType.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])), url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB, RendererC])),
url(r'^html$', HTMLView.as_view()), url(r'^html$', HTMLView.as_view()),
url(r'^html1$', HTMLView1.as_view()), url(r'^html1$', HTMLView1.as_view()),
url(r'^html_new_model$', HTMLNewModelView.as_view()),
url(r'^html_new_model_viewset', include(new_model_viewset_router.urls)),
url(r'^restframework', include('rest_framework.urls', namespace='rest_framework')) url(r'^restframework', include('rest_framework.urls', namespace='rest_framework'))
) )
@ -191,7 +213,21 @@ class Issue122Tests(TestCase):
self.client.get('/html1') self.client.get('/html1')
class Issue807Testts(TestCase): class Issue467Tests(TestCase):
"""
Tests for #467
"""
urls = 'rest_framework.tests.response'
def test_form_has_label_and_help_text(self):
resp = self.client.get('/html_new_model')
self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8')
self.assertContains(resp, 'Text comes here')
self.assertContains(resp, 'Text description.')
class Issue807Tests(TestCase):
""" """
Covers #807 Covers #807
""" """
@ -224,3 +260,19 @@ class Issue807Testts(TestCase):
headers = {"HTTP_ACCEPT": RendererC.media_type} headers = {"HTTP_ACCEPT": RendererC.media_type}
resp = self.client.get('/setbyview', **headers) resp = self.client.get('/setbyview', **headers)
self.assertEqual('setbyview', resp['Content-Type']) self.assertEqual('setbyview', resp['Content-Type'])
def test_viewset_label_help_text(self):
param = '?%s=%s' % (
api_settings.URL_ACCEPT_OVERRIDE,
'text/html'
)
resp = self.client.get('/html_new_model_viewset/' + param)
self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8')
self.assertContains(resp, 'Text comes here')
self.assertContains(resp, 'Text description.')
def test_form_has_label_and_help_text(self):
resp = self.client.get('/html_new_model')
self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8')
self.assertContains(resp, 'Text comes here')
self.assertContains(resp, 'Text description.')

View File

@ -4,10 +4,11 @@ from django.db.models.fields import BLANK_CHOICE_DASH
from django.test import TestCase from django.test import TestCase
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers, fields, relations
from rest_framework.tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel, from rest_framework.tests.models import (HasPositiveIntegerAsChoice, Album, ActionItem, Anchor, BasicModel,
BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel, BlankFieldModel, BlogPost, BlogPostComment, Book, CallableDefaultValueModel, DefaultValueModel,
ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel) ManyToManyModel, Person, ReadOnlyManyToManyModel, Photo, RESTFrameworkModel)
from rest_framework.tests.models import BasicModelSerializer
import datetime import datetime
import pickle import pickle
@ -1324,8 +1325,7 @@ class DeserializeListTestCase(TestCase):
self.assertEqual(serializer.errors, expected) self.assertEqual(serializer.errors, expected)
# test for issue 747 # Test for issue 747
class LazyStringModel(object): class LazyStringModel(object):
def __init__(self, lazystring): def __init__(self, lazystring):
@ -1352,6 +1352,31 @@ class LazyStringsTestCase(TestCase):
type('lazystring')) type('lazystring'))
# Test for issue #467
class FieldLabelTest(TestCase):
def setUp(self):
self.serializer_class = BasicModelSerializer
def test_label_from_model(self):
"""
Validates that label and help_text are correctly copied from the model class.
"""
serializer = self.serializer_class()
text_field = serializer.fields['text']
self.assertEquals('Text comes here', text_field.label)
self.assertEquals('Text description.', text_field.help_text)
def test_field_ctor(self):
"""
This is check that ctor supports both label and help_text.
"""
self.assertEquals('Label', fields.Field(label='Label', help_text='Help').label)
self.assertEquals('Help', fields.CharField(label='Label', help_text='Help').help_text)
self.assertEquals('Label', relations.HyperlinkedRelatedField(view_name='fake', label='Label', help_text='Help', many=True).label)
class AttributeMappingOnAutogeneratedFieldsTests(TestCase): class AttributeMappingOnAutogeneratedFieldsTests(TestCase):
def setUp(self): def setUp(self):