mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 18:08:03 +03:00 
			
		
		
		
	Merge branch 'master' of https://github.com/tomchristie/django-rest-framework
This commit is contained in:
		
						commit
						7557777c66
					
				
							
								
								
									
										6
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								AUTHORS
									
									
									
									
									
								
							| 
						 | 
					@ -34,6 +34,12 @@ Paul Oswald <poswald>
 | 
				
			||||||
Sean C. Farley <scfarley>
 | 
					Sean C. Farley <scfarley>
 | 
				
			||||||
Daniel Izquierdo <izquierdo>
 | 
					Daniel Izquierdo <izquierdo>
 | 
				
			||||||
Can Yavuz <tschan>
 | 
					Can Yavuz <tschan>
 | 
				
			||||||
 | 
					Shawn Lewis <shawnlewis>
 | 
				
			||||||
 | 
					Adam Ness <greylurk>
 | 
				
			||||||
 | 
					<yetist>
 | 
				
			||||||
 | 
					Max Arnold <max-arnold>
 | 
				
			||||||
 | 
					Ralph Broenink <ralphje>
 | 
				
			||||||
 | 
					Simon Pantzare <pilt>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
THANKS TO:
 | 
					THANKS TO:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,11 @@
 | 
				
			||||||
Release Notes
 | 
					Release Notes
 | 
				
			||||||
=============
 | 
					=============
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					0.4.0-dev
 | 
				
			||||||
 | 
					---------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Markdown < 2.0 is no longer supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
0.3.3
 | 
					0.3.3
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ We also have a `Jenkins service <http://jenkins.tibold.nl/job/djangorestframewor
 | 
				
			||||||
Requirements:
 | 
					Requirements:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Python (2.5, 2.6, 2.7 supported)
 | 
					* Python (2.5, 2.6, 2.7 supported)
 | 
				
			||||||
* Django (1.2, 1.3, 1.4-alpha supported)
 | 
					* Django (1.2, 1.3, 1.4 supported)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Installation Notes
 | 
					Installation Notes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
__version__ = '0.3.3'
 | 
					__version__ = '0.4.0-dev'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = __version__  # synonym
 | 
					VERSION = __version__  # synonym
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,15 +65,45 @@ except ImportError:
 | 
				
			||||||
            environ.update(request)
 | 
					            environ.update(request)
 | 
				
			||||||
            return WSGIRequest(environ)
 | 
					            return WSGIRequest(environ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# django.views.generic.View (Django >= 1.3)
 | 
					# django.views.generic.View (1.3 <= Django < 1.4)
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from django.views.generic import View
 | 
					    from django.views.generic import View
 | 
				
			||||||
    if not hasattr(View, 'head'):
 | 
					
 | 
				
			||||||
 | 
					    if django.VERSION < (1, 4):
 | 
				
			||||||
 | 
					        from django.utils.decorators import classonlymethod
 | 
				
			||||||
 | 
					        from django.utils.functional import update_wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # First implementation of Django class-based views did not include head method
 | 
					        # First implementation of Django class-based views did not include head method
 | 
				
			||||||
        # in base View class - https://code.djangoproject.com/ticket/15668
 | 
					        # in base View class - https://code.djangoproject.com/ticket/15668
 | 
				
			||||||
        class ViewPlusHead(View):
 | 
					        class ViewPlusHead(View):
 | 
				
			||||||
            def head(self, request, *args, **kwargs):
 | 
					            @classonlymethod
 | 
				
			||||||
                return self.get(request, *args, **kwargs)
 | 
					            def as_view(cls, **initkwargs):
 | 
				
			||||||
 | 
					                """
 | 
				
			||||||
 | 
					                Main entry point for a request-response process.
 | 
				
			||||||
 | 
					                """
 | 
				
			||||||
 | 
					                # sanitize keyword arguments
 | 
				
			||||||
 | 
					                for key in initkwargs:
 | 
				
			||||||
 | 
					                    if key in cls.http_method_names:
 | 
				
			||||||
 | 
					                        raise TypeError(u"You tried to pass in the %s method name as a "
 | 
				
			||||||
 | 
					                                        u"keyword argument to %s(). Don't do that."
 | 
				
			||||||
 | 
					                                        % (key, cls.__name__))
 | 
				
			||||||
 | 
					                    if not hasattr(cls, key):
 | 
				
			||||||
 | 
					                        raise TypeError(u"%s() received an invalid keyword %r" % (
 | 
				
			||||||
 | 
					                            cls.__name__, key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                def view(request, *args, **kwargs):
 | 
				
			||||||
 | 
					                    self = cls(**initkwargs)
 | 
				
			||||||
 | 
					                    if hasattr(self, 'get') and not hasattr(self, 'head'):
 | 
				
			||||||
 | 
					                        self.head = self.get
 | 
				
			||||||
 | 
					                    return self.dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # take name and docstring from class
 | 
				
			||||||
 | 
					                update_wrapper(view, cls, updated=())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # and possible attributes set by decorators
 | 
				
			||||||
 | 
					                # like csrf_exempt from dispatch
 | 
				
			||||||
 | 
					                update_wrapper(view, cls.dispatch, assigned=())
 | 
				
			||||||
 | 
					                return view
 | 
				
			||||||
        View = ViewPlusHead
 | 
					        View = ViewPlusHead
 | 
				
			||||||
 | 
					
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
| 
						 | 
					@ -121,6 +151,8 @@ except ImportError:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            def view(request, *args, **kwargs):
 | 
					            def view(request, *args, **kwargs):
 | 
				
			||||||
                self = cls(**initkwargs)
 | 
					                self = cls(**initkwargs)
 | 
				
			||||||
 | 
					                if hasattr(self, 'get') and not hasattr(self, 'head'):
 | 
				
			||||||
 | 
					                    self.head = self.get
 | 
				
			||||||
                return self.dispatch(request, *args, **kwargs)
 | 
					                return self.dispatch(request, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # take name and docstring from class
 | 
					            # take name and docstring from class
 | 
				
			||||||
| 
						 | 
					@ -154,9 +186,6 @@ except ImportError:
 | 
				
			||||||
            #)
 | 
					            #)
 | 
				
			||||||
            return http.HttpResponseNotAllowed(allowed_methods)
 | 
					            return http.HttpResponseNotAllowed(allowed_methods)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def head(self, request, *args, **kwargs):
 | 
					 | 
				
			||||||
            return self.get(request, *args, **kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# PUT, DELETE do not require CSRF until 1.4.  They should.  Make it better.
 | 
					# PUT, DELETE do not require CSRF until 1.4.  They should.  Make it better.
 | 
				
			||||||
if django.VERSION >= (1, 4):
 | 
					if django.VERSION >= (1, 4):
 | 
				
			||||||
    from django.middleware.csrf import CsrfViewMiddleware
 | 
					    from django.middleware.csrf import CsrfViewMiddleware
 | 
				
			||||||
| 
						 | 
					@ -370,6 +399,8 @@ else:
 | 
				
			||||||
# Markdown is optional
 | 
					# Markdown is optional
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    import markdown
 | 
					    import markdown
 | 
				
			||||||
 | 
					    if markdown.version_info < (2, 0):
 | 
				
			||||||
 | 
					        raise ImportError('Markdown < 2.0 is not supported.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
 | 
					    class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,7 +181,7 @@ class RequestMixin(object):
 | 
				
			||||||
                return parser.parse(stream)
 | 
					                return parser.parse(stream)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raise ErrorResponse(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
 | 
					        raise ErrorResponse(status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
 | 
				
			||||||
                            {'error': 'Unsupported media type in request \'%s\'.' % 
 | 
					                            {'detail': 'Unsupported media type in request \'%s\'.' % 
 | 
				
			||||||
                            content_type})
 | 
					                            content_type})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
| 
						 | 
					@ -274,7 +274,8 @@ class ResponseMixin(object):
 | 
				
			||||||
            accept_list = [request.GET.get(self._ACCEPT_QUERY_PARAM)]
 | 
					            accept_list = [request.GET.get(self._ACCEPT_QUERY_PARAM)]
 | 
				
			||||||
        elif (self._IGNORE_IE_ACCEPT_HEADER and
 | 
					        elif (self._IGNORE_IE_ACCEPT_HEADER and
 | 
				
			||||||
              'HTTP_USER_AGENT' in request.META and
 | 
					              'HTTP_USER_AGENT' in request.META and
 | 
				
			||||||
              MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT'])):
 | 
					              MSIE_USER_AGENT_REGEX.match(request.META['HTTP_USER_AGENT']) and
 | 
				
			||||||
 | 
					              request.META.get('HTTP_X_REQUESTED_WITH', '') != 'XMLHttpRequest'):
 | 
				
			||||||
            # Ignore MSIE's broken accept behavior and do something sensible instead
 | 
					            # Ignore MSIE's broken accept behavior and do something sensible instead
 | 
				
			||||||
            accept_list = ['text/html', '*/*']
 | 
					            accept_list = ['text/html', '*/*']
 | 
				
			||||||
        elif 'HTTP_ACCEPT' in request.META:
 | 
					        elif 'HTTP_ACCEPT' in request.META:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,6 +182,10 @@ class TemplateRenderer(BaseRenderer):
 | 
				
			||||||
    media_type = None
 | 
					    media_type = None
 | 
				
			||||||
    template = None
 | 
					    template = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, view):
 | 
				
			||||||
 | 
					        super(TemplateRenderer, self).__init__(view)
 | 
				
			||||||
 | 
					        self.template = getattr(self.view, "template", self.template)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, obj=None, media_type=None):
 | 
					    def render(self, obj=None, media_type=None):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Renders *obj* using the :attr:`template` specified on the class.
 | 
					        Renders *obj* using the :attr:`template` specified on the class.
 | 
				
			||||||
| 
						 | 
					@ -202,6 +206,10 @@ class DocumentingTemplateRenderer(BaseRenderer):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template = None
 | 
					    template = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, view):
 | 
				
			||||||
 | 
					        super(DocumentingTemplateRenderer, self).__init__(view)
 | 
				
			||||||
 | 
					        self.template = getattr(self.view, "template", self.template)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_content(self, view, request, obj, media_type):
 | 
					    def _get_content(self, view, request, obj, media_type):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Get the content as if it had been rendered by a non-documenting renderer.
 | 
					        Get the content as if it had been rendered by a non-documenting renderer.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -169,6 +169,7 @@ class FormResource(Resource):
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Add any unknown field errors
 | 
					            # Add any unknown field errors
 | 
				
			||||||
 | 
					            if not self.allow_unknown_form_fields:
 | 
				
			||||||
                for key in unknown_fields:
 | 
					                for key in unknown_fields:
 | 
				
			||||||
                    field_errors[key] = [u'This field does not exist.']
 | 
					                    field_errors[key] = [u'This field does not exist.']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
Customizable serialization.
 | 
					Customizable serialization.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.db.models.query import QuerySet
 | 
					from django.db.models.query import QuerySet, RawQuerySet
 | 
				
			||||||
from django.utils.encoding import smart_unicode, is_protected_type, smart_str
 | 
					from django.utils.encoding import smart_unicode, is_protected_type, smart_str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import inspect
 | 
					import inspect
 | 
				
			||||||
| 
						 | 
					@ -25,16 +25,9 @@ def _field_to_tuple(field):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _fields_to_list(fields):
 | 
					def _fields_to_list(fields):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Return a list of field names.
 | 
					    Return a list of field tuples.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    return [_field_to_tuple(field)[0] for field in fields or ()]
 | 
					    return [_field_to_tuple(field) for field in fields or ()]
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _fields_to_dict(fields):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Return a `dict` of field name -> None, or tuple of fields, or Serializer class
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    return dict([_field_to_tuple(field) for field in fields or ()])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _SkipField(Exception):
 | 
					class _SkipField(Exception):
 | 
				
			||||||
| 
						 | 
					@ -104,15 +97,17 @@ class Serializer(object):
 | 
				
			||||||
    The maximum depth to serialize to, or `None`.
 | 
					    The maximum depth to serialize to, or `None`.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    parent = None
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    A reference to the root serializer when descending down into fields.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, depth=None, stack=[], **kwargs):
 | 
					    def __init__(self, depth=None, stack=[], **kwargs):
 | 
				
			||||||
        if depth is not None:
 | 
					        if depth is not None:
 | 
				
			||||||
            self.depth = depth
 | 
					            self.depth = depth
 | 
				
			||||||
        self.stack = stack
 | 
					        self.stack = stack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_fields(self, obj):
 | 
					    def get_fields(self, obj):
 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Return the set of field names/keys to use for a model instance/dict.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        fields = self.fields
 | 
					        fields = self.fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # If `fields` is not set, we use the default fields and modify
 | 
					        # If `fields` is not set, we use the default fields and modify
 | 
				
			||||||
| 
						 | 
					@ -123,9 +118,6 @@ class Serializer(object):
 | 
				
			||||||
            exclude = self.exclude or ()
 | 
					            exclude = self.exclude or ()
 | 
				
			||||||
            fields = set(default + list(include)) - set(exclude)
 | 
					            fields = set(default + list(include)) - set(exclude)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            fields = _fields_to_list(self.fields)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return fields
 | 
					        return fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_default_fields(self, obj):
 | 
					    def get_default_fields(self, obj):
 | 
				
			||||||
| 
						 | 
					@ -139,15 +131,16 @@ class Serializer(object):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return obj.keys()
 | 
					            return obj.keys()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_related_serializer(self, key):
 | 
					    def get_related_serializer(self, info):
 | 
				
			||||||
        info = _fields_to_dict(self.fields).get(key, None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # If an element in `fields` is a 2-tuple of (str, tuple)
 | 
					        # If an element in `fields` is a 2-tuple of (str, tuple)
 | 
				
			||||||
        # then the second element of the tuple is the fields to
 | 
					        # then the second element of the tuple is the fields to
 | 
				
			||||||
        # set on the related serializer
 | 
					        # set on the related serializer
 | 
				
			||||||
        if isinstance(info, (list, tuple)):
 | 
					
 | 
				
			||||||
        class OnTheFlySerializer(self.__class__):
 | 
					        class OnTheFlySerializer(self.__class__):
 | 
				
			||||||
            fields = info
 | 
					            fields = info
 | 
				
			||||||
 | 
					            parent = getattr(self, 'parent') or self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if isinstance(info, (list, tuple)):
 | 
				
			||||||
            return OnTheFlySerializer
 | 
					            return OnTheFlySerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # If an element in `fields` is a 2-tuple of (str, Serializer)
 | 
					        # If an element in `fields` is a 2-tuple of (str, Serializer)
 | 
				
			||||||
| 
						 | 
					@ -165,8 +158,9 @@ class Serializer(object):
 | 
				
			||||||
        elif isinstance(info, str) and info in _serializers:
 | 
					        elif isinstance(info, str) and info in _serializers:
 | 
				
			||||||
            return _serializers[info]
 | 
					            return _serializers[info]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Otherwise use `related_serializer` or fall back to `Serializer`
 | 
					        # Otherwise use `related_serializer` or fall back to
 | 
				
			||||||
        return getattr(self, 'related_serializer') or Serializer
 | 
					        # `OnTheFlySerializer` preserve custom serialization methods.
 | 
				
			||||||
 | 
					        return getattr(self, 'related_serializer') or OnTheFlySerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def serialize_key(self, key):
 | 
					    def serialize_key(self, key):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -175,11 +169,11 @@ class Serializer(object):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return self.rename.get(smart_str(key), smart_str(key))
 | 
					        return self.rename.get(smart_str(key), smart_str(key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def serialize_val(self, key, obj):
 | 
					    def serialize_val(self, key, obj, related_info):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Convert a model field or dict value into a serializable representation.
 | 
					        Convert a model field or dict value into a serializable representation.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        related_serializer = self.get_related_serializer(key)
 | 
					        related_serializer = self.get_related_serializer(related_info)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.depth is None:
 | 
					        if self.depth is None:
 | 
				
			||||||
            depth = None
 | 
					            depth = None
 | 
				
			||||||
| 
						 | 
					@ -194,7 +188,8 @@ class Serializer(object):
 | 
				
			||||||
            stack = self.stack[:]
 | 
					            stack = self.stack[:]
 | 
				
			||||||
            stack.append(obj)
 | 
					            stack.append(obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return related_serializer(depth=depth, stack=stack).serialize(obj)
 | 
					        return related_serializer(depth=depth, stack=stack).serialize(
 | 
				
			||||||
 | 
					            obj, request=getattr(self, 'request', None))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def serialize_max_depth(self, obj):
 | 
					    def serialize_max_depth(self, obj):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -219,7 +214,7 @@ class Serializer(object):
 | 
				
			||||||
        fields = self.get_fields(instance)
 | 
					        fields = self.get_fields(instance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # serialize each required field
 | 
					        # serialize each required field
 | 
				
			||||||
        for fname in fields:
 | 
					        for fname, related_info in _fields_to_list(fields):
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                # we first check for a method 'fname' on self,
 | 
					                # we first check for a method 'fname' on self,
 | 
				
			||||||
                # 'fname's signature must be 'def fname(self, instance)'
 | 
					                # 'fname's signature must be 'def fname(self, instance)'
 | 
				
			||||||
| 
						 | 
					@ -237,7 +232,7 @@ class Serializer(object):
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                key = self.serialize_key(fname)
 | 
					                key = self.serialize_key(fname)
 | 
				
			||||||
                val = self.serialize_val(fname, obj)
 | 
					                val = self.serialize_val(fname, obj, related_info)
 | 
				
			||||||
                data[key] = val
 | 
					                data[key] = val
 | 
				
			||||||
            except _SkipField:
 | 
					            except _SkipField:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
| 
						 | 
					@ -268,15 +263,19 @@ class Serializer(object):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return smart_unicode(obj, strings_only=True)
 | 
					        return smart_unicode(obj, strings_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def serialize(self, obj):
 | 
					    def serialize(self, obj, request=None):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Convert any object into a serializable representation.
 | 
					        Convert any object into a serializable representation.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Request from related serializer.
 | 
				
			||||||
 | 
					        if request is not None:
 | 
				
			||||||
 | 
					            self.request = request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if isinstance(obj, (dict, models.Model)):
 | 
					        if isinstance(obj, (dict, models.Model)):
 | 
				
			||||||
            # Model instances & dictionaries
 | 
					            # Model instances & dictionaries
 | 
				
			||||||
            return self.serialize_model(obj)
 | 
					            return self.serialize_model(obj)
 | 
				
			||||||
        elif isinstance(obj, (tuple, list, set, QuerySet, types.GeneratorType)):
 | 
					        elif isinstance(obj, (tuple, list, set, QuerySet, RawQuerySet, types.GeneratorType)):
 | 
				
			||||||
            # basic iterables
 | 
					            # basic iterables
 | 
				
			||||||
            return self.serialize_iter(obj)
 | 
					            return self.serialize_iter(obj)
 | 
				
			||||||
        elif isinstance(obj, models.Manager):
 | 
					        elif isinstance(obj, models.Manager):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ register = Library()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def add_query_param(url, param):
 | 
					def add_query_param(url, param):
 | 
				
			||||||
    return unicode(URLObject(url).with_query(param))
 | 
					    return unicode(URLObject(url).add_query_param(*param.split('=')))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
register.filter('add_query_param', add_query_param)
 | 
					register.filter('add_query_param', add_query_param)
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,16 @@ class UserAgentMungingTest(TestCase):
 | 
				
			||||||
            resp = self.view(req)
 | 
					            resp = self.view(req)
 | 
				
			||||||
            self.assertEqual(resp['Content-Type'], 'text/html')
 | 
					            self.assertEqual(resp['Content-Type'], 'text/html')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_dont_munge_msie_with_x_requested_with_header(self):
 | 
				
			||||||
 | 
					        """Send MSIE user agent strings, and an X-Requested-With header, and
 | 
				
			||||||
 | 
					        ensure that we get a JSON response if we set a */* Accept header."""
 | 
				
			||||||
 | 
					        for user_agent in (MSIE_9_USER_AGENT,
 | 
				
			||||||
 | 
					                           MSIE_8_USER_AGENT,
 | 
				
			||||||
 | 
					                           MSIE_7_USER_AGENT):
 | 
				
			||||||
 | 
					            req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
 | 
				
			||||||
 | 
					            resp = self.view(req)
 | 
				
			||||||
 | 
					            self.assertEqual(resp['Content-Type'], 'application/json')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_dont_rewrite_msie_accept_header(self):
 | 
					    def test_dont_rewrite_msie_accept_header(self):
 | 
				
			||||||
        """Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
 | 
					        """Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
 | 
				
			||||||
        that we get a JSON response if we set a */* accept header."""
 | 
					        that we get a JSON response if we set a */* accept header."""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,6 +104,27 @@ class TestFieldNesting(TestCase):
 | 
				
			||||||
        self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
 | 
					        self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
 | 
				
			||||||
        self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
 | 
					        self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_serializer_no_fields(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test related serializer works when the fields attr isn't present. Fix for
 | 
				
			||||||
 | 
					        #178.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        class NestedM2(Serializer):
 | 
				
			||||||
 | 
					            fields = ('field1', )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class NestedM3(Serializer):
 | 
				
			||||||
 | 
					            fields = ('field2', )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class SerializerM2(Serializer):
 | 
				
			||||||
 | 
					            include = [('field', NestedM2)]
 | 
				
			||||||
 | 
					            exclude = ('id', )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class SerializerM3(Serializer):
 | 
				
			||||||
 | 
					            fields = [('field', NestedM3)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
 | 
				
			||||||
 | 
					        self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_serializer_classname_nesting(self):
 | 
					    def test_serializer_classname_nesting(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Test related model serialization
 | 
					        Test related model serialization
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,6 +156,9 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        description = _remove_leading_indent(description)
 | 
					        description = _remove_leading_indent(description)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not isinstance(description, unicode):
 | 
				
			||||||
 | 
					            description = description.decode('UTF-8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if html:
 | 
					        if html:
 | 
				
			||||||
            return self.markup_description(description)
 | 
					            return self.markup_description(description)
 | 
				
			||||||
        return description
 | 
					        return description
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ Alternative frameworks
 | 
				
			||||||
There are a number of alternative REST frameworks for Django:
 | 
					There are a number of alternative REST frameworks for Django:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* `django-piston <https://bitbucket.org/jespern/django-piston/wiki/Home>`_ is very mature, and has a large community behind it.  This project was originally based on piston code in parts.
 | 
					* `django-piston <https://bitbucket.org/jespern/django-piston/wiki/Home>`_ is very mature, and has a large community behind it.  This project was originally based on piston code in parts.
 | 
				
			||||||
* `django-tasypie <https://github.com/toastdriven/django-tastypie>`_ is also very good, and has a very active and helpful developer community and maintainers.
 | 
					* `django-tastypie <https://github.com/toastdriven/django-tastypie>`_ is also very good, and has a very active and helpful developer community and maintainers.
 | 
				
			||||||
* Other interesting projects include `dagny <https://github.com/zacharyvoase/dagny>`_ and `dj-webmachine <http://benoitc.github.com/dj-webmachine/>`_
 | 
					* Other interesting projects include `dagny <https://github.com/zacharyvoase/dagny>`_ and `dj-webmachine <http://benoitc.github.com/dj-webmachine/>`_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
# Documentation requires Django & Sphinx, and their dependencies...
 | 
					# Documentation requires Django & Sphinx, and their dependencies...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Django==1.2.4
 | 
					Django>=1.2.4
 | 
				
			||||||
Jinja2==2.5.5
 | 
					Jinja2==2.5.5
 | 
				
			||||||
Pygments==1.4
 | 
					Pygments==1.4
 | 
				
			||||||
Sphinx==1.0.7
 | 
					Sphinx==1.0.7
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,11 +12,11 @@ class PermissionsExampleView(View):
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                'name': 'Throttling Example',
 | 
					                'name': 'Throttling Example',
 | 
				
			||||||
                'url': reverse('throttled-resource', request)
 | 
					                'url': reverse('throttled-resource', request=request)
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                'name': 'Logged in example',
 | 
					                'name': 'Logged in example',
 | 
				
			||||||
                'url': reverse('loggedin-resource', request)
 | 
					                'url': reverse('loggedin-resource', request=request)
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@ class PygmentsRoot(View):
 | 
				
			||||||
        Return a list of all currently existing snippets.
 | 
					        Return a list of all currently existing snippets.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        unique_ids = [os.path.split(f)[1] for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
 | 
					        unique_ids = [os.path.split(f)[1] for f in list_dir_sorted_by_ctime(HIGHLIGHTED_CODE_DIR)]
 | 
				
			||||||
        return [reverse('pygments-instance', request, args=[unique_id]) for unique_id in unique_ids]
 | 
					        return [reverse('pygments-instance', request=request, args=[unique_id]) for unique_id in unique_ids]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request):
 | 
					    def post(self, request):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
| 
						 | 
					@ -85,7 +85,7 @@ class PygmentsRoot(View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
 | 
					        remove_oldest_files(HIGHLIGHTED_CODE_DIR, MAX_FILES)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request, args=[unique_id])})
 | 
					        return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', request=request, args=[unique_id])})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PygmentsInstance(View):
 | 
					class PygmentsInstance(View):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
					@ -64,6 +64,9 @@ setup(
 | 
				
			||||||
    package_data=get_package_data('djangorestframework'),
 | 
					    package_data=get_package_data('djangorestframework'),
 | 
				
			||||||
    test_suite='djangorestframework.runtests.runcoverage.main',
 | 
					    test_suite='djangorestframework.runtests.runcoverage.main',
 | 
				
			||||||
    install_requires=['URLObject>=0.6.0'],
 | 
					    install_requires=['URLObject>=0.6.0'],
 | 
				
			||||||
 | 
					    extras_require={
 | 
				
			||||||
 | 
					        'markdown': ["Markdown>=2.0"]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    classifiers=[
 | 
					    classifiers=[
 | 
				
			||||||
        'Development Status :: 4 - Beta',
 | 
					        'Development Status :: 4 - Beta',
 | 
				
			||||||
        'Environment :: Web Environment',
 | 
					        'Environment :: Web Environment',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user