Merge pull request #321 from tomchristie/pastebin_tutorial

Tweaks and fixes in order to support the pastebin tutorial
This commit is contained in:
Tom Christie 2012-10-26 05:22:34 -07:00
commit 7cdedc0f71
6 changed files with 32 additions and 23 deletions

View File

@ -30,7 +30,7 @@ For more complex cases you might also want to override various methods on the vi
serializer_class = UserSerializer serializer_class = UserSerializer
permission_classes = (IsAdminUser,) permission_classes = (IsAdminUser,)
def get_paginate_by(self): def get_paginate_by(self, queryset):
""" """
Use smaller pagination for HTML representations. Use smaller pagination for HTML representations.
""" """

View File

@ -258,7 +258,7 @@ class ManyRelatedMixin(object):
def field_from_native(self, data, field_name, into): def field_from_native(self, data, field_name, into):
if self.readonly: if self.readonly:
return return
try: try:
# Form data # Form data
value = data.getlist(self.source or field_name) value = data.getlist(self.source or field_name)

View File

@ -224,7 +224,7 @@ class BrowsableAPIRenderer(BaseRenderer):
return content return content
def show_form_for_method(self, view, method, request): def show_form_for_method(self, view, method, request, obj):
""" """
Returns True if a form should be shown for this method. Returns True if a form should be shown for this method.
""" """
@ -236,7 +236,7 @@ class BrowsableAPIRenderer(BaseRenderer):
request = clone_request(request, method) request = clone_request(request, method)
try: try:
if not view.has_permission(request): if not view.has_permission(request, obj):
return # Don't have permission return # Don't have permission
except: except:
return # Don't have permission and exception explicitly raise return # Don't have permission and exception explicitly raise
@ -295,7 +295,8 @@ class BrowsableAPIRenderer(BaseRenderer):
In the absence on of the Resource having an associated form then In the absence on of the Resource having an associated form then
provide a form that can be used to submit arbitrary content. provide a form that can be used to submit arbitrary content.
""" """
if not self.show_form_for_method(view, method, request): obj = getattr(view, 'object', None)
if not self.show_form_for_method(view, method, request, obj):
return return
if method == 'DELETE' or method == 'OPTIONS': if method == 'DELETE' or method == 'OPTIONS':
@ -305,17 +306,13 @@ class BrowsableAPIRenderer(BaseRenderer):
media_types = [parser.media_type for parser in view.parser_classes] media_types = [parser.media_type for parser in view.parser_classes]
return self.get_generic_content_form(media_types) return self.get_generic_content_form(media_types)
# Creating an on the fly form see: http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
obj, data = None, None
if getattr(view, 'object', None):
obj = view.object
serializer = view.get_serializer(instance=obj) serializer = view.get_serializer(instance=obj)
fields = self.serializer_to_form_fields(serializer) fields = self.serializer_to_form_fields(serializer)
# Creating an on the fly form see:
# http://stackoverflow.com/questions/3915024/dynamically-creating-classes-python
OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields) OnTheFlyForm = type("OnTheFlyForm", (forms.Form,), fields)
if obj: data = (obj is not None) and serializer.data or None
data = serializer.data
form_instance = OnTheFlyForm(data) form_instance = OnTheFlyForm(data)
return form_instance return form_instance

View File

@ -3,6 +3,7 @@ import datetime
import types import types
from decimal import Decimal from decimal import Decimal
from django.db import models from django.db import models
from django.forms import widgets
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from rest_framework.compat import get_concrete_model from rest_framework.compat import get_concrete_model
from rest_framework.fields import * from rest_framework.fields import *
@ -409,6 +410,15 @@ class ModelSerializer(Serializer):
kwargs = {} kwargs = {}
if model_field.has_default(): if model_field.has_default():
kwargs['required'] = False kwargs['required'] = False
kwargs['default'] = model_field.get_default()
if model_field.__class__ == models.TextField:
kwargs['widget'] = widgets.Textarea
# TODO: TypedChoiceField?
if model_field.flatchoices: # This ModelField contains choices
kwargs['choices'] = model_field.flatchoices
return ChoiceField(**kwargs)
field_mapping = { field_mapping = {
models.FloatField: FloatField, models.FloatField: FloatField,

View File

@ -62,12 +62,12 @@ class CallableDefaultValueModel(RESTFrameworkModel):
class ManyToManyModel(RESTFrameworkModel): class ManyToManyModel(RESTFrameworkModel):
rel = models.ManyToManyField(Anchor) rel = models.ManyToManyField(Anchor)
class ReadOnlyManyToManyModel(RESTFrameworkModel): class ReadOnlyManyToManyModel(RESTFrameworkModel):
text = models.CharField(max_length=100, default='anchor') text = models.CharField(max_length=100, default='anchor')
rel = models.ManyToManyField(Anchor) rel = models.ManyToManyField(Anchor)
# Models to test generic relations # Models to test generic relations

View File

@ -7,7 +7,7 @@ from rest_framework.tests.models import *
class SubComment(object): class SubComment(object):
def __init__(self, sub_comment): def __init__(self, sub_comment):
self.sub_comment = sub_comment self.sub_comment = sub_comment
class Comment(object): class Comment(object):
def __init__(self, email, content, created): def __init__(self, email, content, created):
@ -18,7 +18,7 @@ class Comment(object):
def __eq__(self, other): def __eq__(self, other):
return all([getattr(self, attr) == getattr(other, attr) return all([getattr(self, attr) == getattr(other, attr)
for attr in ('email', 'content', 'created')]) for attr in ('email', 'content', 'created')])
def get_sub_comment(self): def get_sub_comment(self):
sub_comment = SubComment('And Merry Christmas!') sub_comment = SubComment('And Merry Christmas!')
return sub_comment return sub_comment
@ -29,7 +29,7 @@ class CommentSerializer(serializers.Serializer):
content = serializers.CharField(max_length=1000) content = serializers.CharField(max_length=1000)
created = serializers.DateTimeField() created = serializers.DateTimeField()
sub_comment = serializers.Field(source='get_sub_comment.sub_comment') sub_comment = serializers.Field(source='get_sub_comment.sub_comment')
def restore_object(self, data, instance=None): def restore_object(self, data, instance=None):
if instance is None: if instance is None:
return Comment(**data) return Comment(**data)
@ -42,6 +42,7 @@ class ActionItemSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = ActionItem model = ActionItem
class BasicTests(TestCase): class BasicTests(TestCase):
def setUp(self): def setUp(self):
self.comment = Comment( self.comment = Comment(
@ -73,7 +74,7 @@ class BasicTests(TestCase):
self.assertEquals(serializer.data, expected) self.assertEquals(serializer.data, expected)
def test_retrieve(self): def test_retrieve(self):
serializer = CommentSerializer(instance=self.comment) serializer = CommentSerializer(instance=self.comment)
self.assertEquals(serializer.data, self.expected) self.assertEquals(serializer.data, self.expected)
def test_create(self): def test_create(self):
@ -104,7 +105,7 @@ class ValidationTests(TestCase):
'email': 'tom@example.com', 'email': 'tom@example.com',
'content': 'x' * 1001, 'content': 'x' * 1001,
'created': datetime.datetime(2012, 1, 1) 'created': datetime.datetime(2012, 1, 1)
} }
self.actionitem = ActionItem('Some to do item', self.actionitem = ActionItem('Some to do item',
) )
@ -131,7 +132,7 @@ class ValidationTests(TestCase):
"""Make sure that a boolean value with a 'False' value is not """Make sure that a boolean value with a 'False' value is not
mistaken for not having a default.""" mistaken for not having a default."""
data = { data = {
'title':'Some action item', 'title': 'Some action item',
#No 'done' value. #No 'done' value.
} }
serializer = ActionItemSerializer(data, instance=self.actionitem) serializer = ActionItemSerializer(data, instance=self.actionitem)
@ -295,11 +296,13 @@ class ManyToManyTests(TestCase):
self.assertEquals(len(ManyToManyModel.objects.all()), 2) self.assertEquals(len(ManyToManyModel.objects.all()), 2)
self.assertEquals(instance.pk, 2) self.assertEquals(instance.pk, 2)
self.assertEquals(list(instance.rel.all()), []) self.assertEquals(list(instance.rel.all()), [])
class ReadOnlyManyToManyTests(TestCase): class ReadOnlyManyToManyTests(TestCase):
def setUp(self): def setUp(self):
class ReadOnlyManyToManySerializer(serializers.ModelSerializer): class ReadOnlyManyToManySerializer(serializers.ModelSerializer):
rel = serializers.ManyRelatedField(readonly=True) rel = serializers.ManyRelatedField(readonly=True)
class Meta: class Meta:
model = ReadOnlyManyToManyModel model = ReadOnlyManyToManyModel
@ -317,7 +320,6 @@ class ReadOnlyManyToManyTests(TestCase):
# A serialized representation of the model instance # A serialized representation of the model instance
self.data = {'rel': [self.anchor.id], 'id': 1, 'text': 'anchor'} self.data = {'rel': [self.anchor.id], 'id': 1, 'text': 'anchor'}
def test_update(self): def test_update(self):
""" """
Attempt to update an instance of a model with a ManyToMany Attempt to update an instance of a model with a ManyToMany