Merge commit '3b258d69c92e9d9293f7c5d1690f0ca434e677e3' into file_and_image_fields

This commit is contained in:
Marko Tibold 2012-11-15 22:48:22 +01:00
commit 403886b79b
17 changed files with 57 additions and 23 deletions

View File

@ -139,7 +139,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[twitter]: https://twitter.com/_tomchristie
[0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X
[sandbox]: http://restframework.herokuapp.com/
[rest-framework-2-announcement]: topics/rest-framework-2-announcement.md
[rest-framework-2-announcement]: http://django-rest-framework.org/topics/rest-framework-2-announcement.html
[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
[docs]: http://django-rest-framework.org/

View File

@ -97,6 +97,21 @@ If successfully authenticated, `TokenAuthentication` provides the following cred
**Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only.
If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal.
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
If you've already created some User`'s, you can run a script like this.
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
for user in User.objects.all():
Token.objects.get_or_create(user=user)
## OAuthAuthentication
This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAuth is appropriate for server-server setups, such as when you want to allow a third-party service to access your API on a user's behalf.

View File

@ -59,6 +59,7 @@ The following people have helped make REST framework great.
* Toni Michel - [tonimichel]
* Ben Konrath - [benkonrath]
* Marc Aymerich - [glic3rinu]
* Ludwig Kraatz - [ludwigkraatz]
Many thanks to everyone who's contributed to the project.
@ -153,3 +154,4 @@ To contact the author directly:
[tonimichel]: https://github.com/tonimichel
[benkonrath]: https://github.com/benkonrath
[glic3rinu]: https://github.com/glic3rinu
[ludwigkraatz]: https://github.com/ludwigkraatz

View File

@ -1,6 +1,6 @@
"""
The `compat` module provides support for backwards compatibility with older
versions of django/python, and compatbility wrappers around optional packages.
versions of django/python, and compatibility wrappers around optional packages.
"""
# flake8: noqa
import django

View File

@ -17,7 +17,7 @@ def api_view(http_method_names):
)
# Note, the above allows us to set the docstring.
# It is the equivelent of:
# It is the equivalent of:
#
# class WrappedAPIView(APIView):
# pass

View File

@ -323,7 +323,7 @@ class RelatedField(WritableField):
choices = property(_get_choices, _set_choices)
### Regular serializier stuff...
### Regular serializer stuff...
def field_to_native(self, obj, field_name):
value = getattr(obj, self.source or field_name)

View File

@ -91,11 +91,11 @@ class SingleObjectAPIView(SingleObjectMixin, GenericAPIView):
pk_url_kwarg = 'pk' # Not provided in Django 1.3
slug_url_kwarg = 'slug' # Not provided in Django 1.3
def get_object(self):
def get_object(self, queryset=None):
"""
Override default to add support for object-level permissions.
"""
obj = super(SingleObjectAPIView, self).get_object()
obj = super(SingleObjectAPIView, self).get_object(queryset)
if not self.has_permission(self.request, obj):
self.permission_denied(self.request)
return obj

View File

@ -19,9 +19,16 @@ class CreateModelMixin(object):
if serializer.is_valid():
self.pre_save(serializer.object)
self.object = serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get_success_headers(self, data):
if 'url' in data:
return {'Location': data.get('url')}
else:
return {}
def pre_save(self, obj):
pass

View File

@ -4,7 +4,7 @@ Renderers are used to serialize a response into specific media types.
They give us a generic way of being able to handle various media types
on the response, such as JSON encoded data or HTML output.
REST framework also provides an HTML renderer the renders the browseable API.
REST framework also provides an HTML renderer the renders the browsable API.
"""
import copy
import string

View File

@ -15,14 +15,17 @@ class Response(SimpleTemplateResponse):
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be defered,
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super(Response, self).__init__(None, status=status)
self.data = data
self.headers = headers and headers[:] or []
self.template_name = template_name
self.exception = exception
if headers:
for name,value in headers.iteritems():
self[name] = value
@property
def rendered_content(self):

View File

@ -89,7 +89,7 @@ class BaseSerializer(Field):
pass
_options_class = SerializerOptions
_dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatability with unsorted implementations.
_dict_class = SortedDictWithMetadata # Set to unsorted dict for backwards compatibility with unsorted implementations.
def __init__(self, instance=None, data=None, files=None, context=None, **kwargs):
super(BaseSerializer, self).__init__(**kwargs)
@ -165,7 +165,7 @@ class BaseSerializer(Field):
self.opts.depth = parent.opts.depth - 1
#####
# Methods to convert or revert from objects <--> primative representations.
# Methods to convert or revert from objects <--> primitive representations.
def get_field_key(self, field_name):
"""
@ -246,7 +246,7 @@ class BaseSerializer(Field):
def to_native(self, obj):
"""
Serialize objects -> primatives.
Serialize objects -> primitives.
"""
if hasattr(obj, '__iter__'):
return [self.convert_object(item) for item in obj]
@ -254,7 +254,7 @@ class BaseSerializer(Field):
def from_native(self, data, files):
"""
Deserialize primatives -> objects.
Deserialize primitives -> objects.
"""
if hasattr(data, '__iter__') and not isinstance(data, dict):
# TODO: error data when deserializing lists
@ -336,7 +336,7 @@ class ModelSerializer(Serializer):
"""
Return all the fields that should be serialized for the model.
"""
# TODO: Modfiy this so that it's called on init, and drop
# TODO: Modify this so that it's called on init, and drop
# serialize/obj/data arguments.
#
# We *could* provide a hook for dynamic fields, but

View File

@ -152,7 +152,7 @@ class APISettings(object):
def validate_setting(self, attr, val):
if attr == 'FILTER_BACKEND' and val is not None:
# Make sure we can initilize the class
# Make sure we can initialize the class
val()
api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS)

View File

@ -8,12 +8,13 @@ factory = RequestFactory()
class BlogPostCommentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='blogpostcomment-detail')
text = serializers.CharField()
blog_post_url = serializers.HyperlinkedRelatedField(source='blog_post', view_name='blogpost-detail')
class Meta:
model = BlogPostComment
fields = ('text', 'blog_post_url')
fields = ('text', 'blog_post_url', 'url')
class PhotoSerializer(serializers.Serializer):
@ -53,6 +54,9 @@ class BlogPostCommentListCreate(generics.ListCreateAPIView):
model = BlogPostComment
serializer_class = BlogPostCommentSerializer
class BlogPostCommentDetail(generics.RetrieveAPIView):
model = BlogPostComment
serializer_class = BlogPostCommentSerializer
class BlogPostDetail(generics.RetrieveAPIView):
model = BlogPost
@ -80,6 +84,7 @@ urlpatterns = patterns('',
url(r'^manytomany/(?P<pk>\d+)/$', ManyToManyDetail.as_view(), name='manytomanymodel-detail'),
url(r'^posts/(?P<pk>\d+)/$', BlogPostDetail.as_view(), name='blogpost-detail'),
url(r'^comments/$', BlogPostCommentListCreate.as_view(), name='blogpostcomment-list'),
url(r'^comments/(?P<pk>\d+)/$', BlogPostCommentDetail.as_view(), name='blogpostcomment-detail'),
url(r'^albums/(?P<title>\w[\w-]*)/$', AlbumDetail.as_view(), name='album-detail'),
url(r'^photos/$', PhotoListCreate.as_view(), name='photo-list'),
url(r'^optionalrelation/(?P<pk>\d+)/$', OptionalRelationDetail.as_view(), name='optionalrelationmodel-detail'),
@ -191,6 +196,7 @@ class TestCreateWithForeignKeys(TestCase):
request = factory.post('/comments/', data=data)
response = self.create_view(request).render()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(response['Location'], 'http://testserver/comments/1/')
self.assertEqual(self.post.blogpostcomment_set.count(), 1)
self.assertEqual(self.post.blogpostcomment_set.all()[0].text, 'A test comment')
@ -215,6 +221,7 @@ class TestCreateWithForeignKeysAndCustomSlug(TestCase):
request = factory.post('/photos/', data=data)
response = self.list_create_view(request).render()
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertNotIn('Location', response, msg='Location should only be included if there is a "url" field on the serializer')
self.assertEqual(self.post.photo_set.count(), 1)
self.assertEqual(self.post.photo_set.all()[0].description, 'A test photo')

View File

@ -106,7 +106,7 @@ class ThrottlingTests(TestCase):
if expect is not None:
self.assertEquals(response['X-Throttle-Wait-Seconds'], expect)
else:
self.assertFalse('X-Throttle-Wait-Seconds' in response.headers)
self.assertFalse('X-Throttle-Wait-Seconds' in response)
def test_seconds_fields(self):
"""

View File

@ -4,7 +4,7 @@ from rest_framework.settings import api_settings
def format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None):
"""
Supplement existing urlpatterns with corrosponding patterns that also
Supplement existing urlpatterns with corresponding patterns that also
include a '.format' suffix. Retains urlpattern ordering.
urlpatterns:

View File

@ -1,7 +1,7 @@
"""
Login and logout views for the browseable API.
Login and logout views for the browsable API.
Add these to your root URLconf if you're using the browseable API and
Add these to your root URLconf if you're using the browsable API and
your API requires authentication.
The urls must be namespaced as 'rest_framework', and you should make sure

View File

@ -140,7 +140,7 @@ class APIView(View):
def http_method_not_allowed(self, request, *args, **kwargs):
"""
Called if `request.method` does not corrospond to a handler method.
Called if `request.method` does not correspond to a handler method.
"""
raise exceptions.MethodNotAllowed(request.method)