aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/api-guide/fields.md20
-rw-r--r--rest_framework/fields.py4
-rw-r--r--rest_framework/generics.py2
-rw-r--r--rest_framework/mixins.py27
-rw-r--r--rest_framework/tests/decorators.py14
-rw-r--r--rest_framework/tests/generics.py15
6 files changed, 79 insertions, 3 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 009d2a79..5f3aba9a 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -6,14 +6,30 @@
>
> — [The Zen of Python][cite]
+**Note:** The serializer fields are declared in fields.py, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.<FieldName>`.
+
+Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects.
+
# Generic Fields
## Field
+A generic, read-only field. You can use this field for any attribute that does not need to support write operations.
+
+## WritableField
+
+A field that supports both read and
+
## ModelField
+A generic field that can be tied to any arbitrary model field. The `ModelField` class delegates the task of serialization/deserialization to it's associated model field. This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field.
+
+**Signature:** `ModelField(model_field=<Django ModelField class>)`
+
# Typed Fields
+These fields represent basic datatypes, and support both reading and writing values.
+
## BooleanField
## CharField
@@ -30,7 +46,7 @@
# Relational Fields
-Relational fields are used to represent model relationships.
+Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`.
## PrimaryKeyRelatedField
@@ -40,4 +56,6 @@ Relational fields are used to represent model relationships.
## ManyHyperlinkedRelatedField
+## HyperLinkedIdentityField
+
[cite]: http://www.python.org/dev/peps/pep-0020/
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 5ce76817..336eac1e 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -341,6 +341,7 @@ class HyperlinkedRelatedField(RelatedField):
def from_native(self, value):
# Convert URL -> model instance pk
+ # TODO: Use values_list
try:
match = resolve(value)
except:
@@ -379,7 +380,8 @@ class HyperlinkedIdentityField(Field):
A field that represents the model's identity using a hyperlink.
"""
def __init__(self, *args, **kwargs):
- # TODO: Make this mandatory
+ # TODO: Make this mandatory, and have the HyperlinkedModelSerializer
+ # set it on-the-fly
self.view_name = kwargs.pop('view_name', None)
super(HyperlinkedIdentityField, self).__init__(*args, **kwargs)
diff --git a/rest_framework/generics.py b/rest_framework/generics.py
index 44f677cc..51874f28 100644
--- a/rest_framework/generics.py
+++ b/rest_framework/generics.py
@@ -79,6 +79,8 @@ class SingleObjectBaseView(SingleObjectMixin, BaseView):
"""
Base class for generic views onto a model instance.
"""
+ pk_url_kwarg = 'pk' # Not provided in Django 1.3
+ slug_url_kwarg = 'slug' # Not provided in Django 1.3
def get_object(self):
"""
diff --git a/rest_framework/mixins.py b/rest_framework/mixins.py
index e3c7cf03..46821f64 100644
--- a/rest_framework/mixins.py
+++ b/rest_framework/mixins.py
@@ -71,13 +71,38 @@ class UpdateModelMixin(object):
Should be mixed in with `SingleObjectBaseView`.
"""
def update(self, request, *args, **kwargs):
- self.object = self.get_object()
+ try:
+ self.object = self.get_object()
+ except Http404:
+ self.object = None
+
serializer = self.get_serializer(data=request.DATA, instance=self.object)
if serializer.is_valid():
+ if self.object is None:
+ obj = serializer.object
+ # TODO: Make ModelSerializers return regular instances,
+ # not DeserializedObject
+ if hasattr(obj, 'object'):
+ obj = obj.object
+ self.update_urlconf_attributes(serializer.object.object)
self.object = serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+ def update_urlconf_attributes(self, obj):
+ """
+ When update (re)creates an object, we need to set any attributes that
+ are tied to the URLconf.
+ """
+ pk = self.kwargs.get(self.pk_url_kwarg, None)
+ if pk:
+ setattr(obj, 'pk', pk)
+
+ slug = self.kwargs.get(self.slug_url_kwarg, None)
+ if slug:
+ slug_field = self.get_slug_field()
+ setattr(obj, slug_field, slug)
+
class DestroyModelMixin(object):
"""
diff --git a/rest_framework/tests/decorators.py b/rest_framework/tests/decorators.py
index a3217bd6..41864d71 100644
--- a/rest_framework/tests/decorators.py
+++ b/rest_framework/tests/decorators.py
@@ -49,6 +49,20 @@ class DecoratorTestCase(TestCase):
response = view(request)
self.assertEqual(response.status_code, 405)
+ def test_calling_put_method(self):
+
+ @api_view(['GET', 'PUT'])
+ def view(request):
+ return Response({})
+
+ request = self.factory.put('/')
+ response = view(request)
+ self.assertEqual(response.status_code, 200)
+
+ request = self.factory.post('/')
+ response = view(request)
+ self.assertEqual(response.status_code, 405)
+
def test_renderer_classes(self):
@api_view(['GET'])
diff --git a/rest_framework/tests/generics.py b/rest_framework/tests/generics.py
index c0645d6e..2a6a0744 100644
--- a/rest_framework/tests/generics.py
+++ b/rest_framework/tests/generics.py
@@ -208,3 +208,18 @@ class TestInstanceView(TestCase):
self.assertEquals(response.data, {'id': 1, 'text': 'foobar'})
updated = self.objects.get(id=1)
self.assertEquals(updated.text, 'foobar')
+
+ def test_put_to_deleted_instance(self):
+ """
+ PUT requests to RetrieveUpdateDestroyAPIView should create an object
+ if it does not currently exist.
+ """
+ self.objects.get(id=1).delete()
+ content = {'text': 'foobar'}
+ request = factory.put('/1', json.dumps(content),
+ content_type='application/json')
+ response = self.view(request, pk=1).render()
+ self.assertEquals(response.status_code, status.HTTP_200_OK)
+ self.assertEquals(response.data, {'id': 1, 'text': 'foobar'})
+ updated = self.objects.get(id=1)
+ self.assertEquals(updated.text, 'foobar')