mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-10-31 07:57:55 +03:00 
			
		
		
		
	Merge remote-tracking branch 'reference/master' into feature/django_1_7
This commit is contained in:
		
						commit
						b2f0f4fcf4
					
				|  | @ -18,7 +18,7 @@ The handled exceptions are: | ||||||
| 
 | 
 | ||||||
| In each case, REST framework will return a response with an appropriate status code and content-type.  The body of the response will include any additional details regarding the nature of the error. | In each case, REST framework will return a response with an appropriate status code and content-type.  The body of the response will include any additional details regarding the nature of the error. | ||||||
| 
 | 
 | ||||||
| By default all error responses will include a key `details` in the body of the response, but other keys may also be included. | By default all error responses will include a key `detail` in the body of the response, but other keys may also be included. | ||||||
| 
 | 
 | ||||||
| For example, the following request: | For example, the following request: | ||||||
| 
 | 
 | ||||||
|  | @ -86,7 +86,7 @@ Note that the exception handler will only be called for responses generated by r | ||||||
| 
 | 
 | ||||||
| The **base class** for all exceptions raised inside REST framework. | The **base class** for all exceptions raised inside REST framework. | ||||||
| 
 | 
 | ||||||
| To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class. | To provide a custom exception, subclass `APIException` and set the `.status_code` and `.default_detail` properties on the class. | ||||||
| 
 | 
 | ||||||
| For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code.  You could do this like so: | For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code.  You could do this like so: | ||||||
| 
 | 
 | ||||||
|  | @ -94,7 +94,7 @@ For example, if your API relies on a third party service that may sometimes be u | ||||||
| 
 | 
 | ||||||
|     class ServiceUnavailable(APIException): |     class ServiceUnavailable(APIException): | ||||||
|         status_code = 503 |         status_code = 503 | ||||||
|         detail = 'Service temporarily unavailable, try again later.' |         default_detail = 'Service temporarily unavailable, try again later.' | ||||||
| 
 | 
 | ||||||
| ## ParseError | ## ParseError | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -104,6 +104,7 @@ A serializer definition that looked like this: | ||||||
|         expired = serializers.Field(source='has_expired') |         expired = serializers.Field(source='has_expired') | ||||||
|          |          | ||||||
|         class Meta: |         class Meta: | ||||||
|  |             model = Account | ||||||
|             fields = ('url', 'owner', 'name', 'expired') |             fields = ('url', 'owner', 'name', 'expired') | ||||||
| 
 | 
 | ||||||
| Would produce output similar to: | Would produce output similar to: | ||||||
|  | @ -125,7 +126,7 @@ A field that supports both read and write operations.  By itself `WritableField` | ||||||
| 
 | 
 | ||||||
| ## ModelField | ## 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. | A generic field that can be tied to any arbitrary model field.  The `ModelField` class delegates the task of serialization/deserialization to its 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. | ||||||
| 
 | 
 | ||||||
| The `ModelField` class is generally intended for internal use, but can be used by your API if needed.  In order to properly instantiate a `ModelField`, it must be passed a field that is attached to an instantiated model.  For example: `ModelField(model_field=MyModel()._meta.get_field('custom_field'))` | The `ModelField` class is generally intended for internal use, but can be used by your API if needed.  In order to properly instantiate a `ModelField`, it must be passed a field that is attached to an instantiated model.  For example: `ModelField(model_field=MyModel()._meta.get_field('custom_field'))` | ||||||
| 
 | 
 | ||||||
|  | @ -307,7 +308,7 @@ Django's regular [FILE_UPLOAD_HANDLERS] are used for handling uploaded files. | ||||||
| 
 | 
 | ||||||
| If you want to create a custom field, you'll probably want to override either one or both of the `.to_native()` and `.from_native()` methods.  These two methods are used to convert between the initial datatype, and a primitive, serializable datatype.  Primitive datatypes may be any of a number, string, date/time/datetime or None.  They may also be any list or dictionary like object that only contains other primitive objects. | If you want to create a custom field, you'll probably want to override either one or both of the `.to_native()` and `.from_native()` methods.  These two methods are used to convert between the initial datatype, and a primitive, serializable datatype.  Primitive datatypes may be any of a number, string, date/time/datetime or None.  They may also be any list or dictionary like object that only contains other primitive objects. | ||||||
| 
 | 
 | ||||||
| The `.to_native()` method is called to convert the initial datatype into a primitive, serializable datatype.  The `from_native()` method is called to restore a primitive datatype into it's initial representation. | The `.to_native()` method is called to convert the initial datatype into a primitive, serializable datatype.  The `from_native()` method is called to restore a primitive datatype into its initial representation. | ||||||
| 
 | 
 | ||||||
| ## Examples | ## Examples | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -119,7 +119,7 @@ For example: | ||||||
|         self.check_object_permissions(self.request, obj) |         self.check_object_permissions(self.request, obj) | ||||||
|         return obj |         return obj | ||||||
| 
 | 
 | ||||||
| Note that if your API doesn't include any object level permissions, you may optionally exclude the ``self.check_object_permissions, and simply return the object from the `get_object_or_404` lookup. | Note that if your API doesn't include any object level permissions, you may optionally exclude the `self.check_object_permissions`, and simply return the object from the `get_object_or_404` lookup. | ||||||
| 
 | 
 | ||||||
| #### `get_filter_backends(self)` | #### `get_filter_backends(self)` | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -218,12 +218,12 @@ You can use any of REST framework's test case classes as you would for the regul | ||||||
| 
 | 
 | ||||||
| When checking the validity of test responses it's often more convenient to inspect the data that the response was created with, rather than inspecting the fully rendered response. | When checking the validity of test responses it's often more convenient to inspect the data that the response was created with, rather than inspecting the fully rendered response. | ||||||
| 
 | 
 | ||||||
| For example, it's easier to inspect `request.data`: | For example, it's easier to inspect `response.data`: | ||||||
| 
 | 
 | ||||||
|     response = self.client.get('/users/4/') |     response = self.client.get('/users/4/') | ||||||
|     self.assertEqual(response.data, {'id': 4, 'username': 'lauren'}) |     self.assertEqual(response.data, {'id': 4, 'username': 'lauren'}) | ||||||
| 
 | 
 | ||||||
| Instead of inspecting the result of parsing `request.content`: | Instead of inspecting the result of parsing `response.content`: | ||||||
| 
 | 
 | ||||||
|     response = self.client.get('/users/4/') |     response = self.client.get('/users/4/') | ||||||
|     self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'}) |     self.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'}) | ||||||
|  |  | ||||||
|  | @ -150,7 +150,7 @@ For example, given the following views... | ||||||
| 
 | 
 | ||||||
|     REST_FRAMEWORK = { |     REST_FRAMEWORK = { | ||||||
|         'DEFAULT_THROTTLE_CLASSES': ( |         'DEFAULT_THROTTLE_CLASSES': ( | ||||||
|             'rest_framework.throttling.ScopedRateThrottle' |             'rest_framework.throttling.ScopedRateThrottle', | ||||||
|         ), |         ), | ||||||
|         'DEFAULT_THROTTLE_RATES': { |         'DEFAULT_THROTTLE_RATES': { | ||||||
|             'contacts': '1000/day', |             'contacts': '1000/day', | ||||||
|  |  | ||||||
|  | @ -225,7 +225,7 @@ To create a base viewset class that provides `create`, `list` and `retrieve` ope | ||||||
|                                     mixins.RetrieveModelMixin, |                                     mixins.RetrieveModelMixin, | ||||||
|                                     viewsets.GenericViewSet): |                                     viewsets.GenericViewSet): | ||||||
|         """ |         """ | ||||||
|         A viewset that provides `retrieve`, `update`, and `list` actions. |         A viewset that provides `retrieve`, `create`, and `list` actions. | ||||||
| 
 | 
 | ||||||
|         To use it, override the class and set the `.queryset` and |         To use it, override the class and set the `.queryset` and | ||||||
|         `.serializer_class` attributes. |         `.serializer_class` attributes. | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ To run the tests, clone the repository, and then: | ||||||
| 
 | 
 | ||||||
|     # Setup the virtual environment |     # Setup the virtual environment | ||||||
|     virtualenv env |     virtualenv env | ||||||
|     env/bin/activate |     source env/bin/activate | ||||||
|     pip install -r requirements.txt |     pip install -r requirements.txt | ||||||
|     pip install -r optionals.txt |     pip install -r optionals.txt | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -182,6 +182,7 @@ The following people have helped make REST framework great. | ||||||
| * Ian Foote - [ian-foote] | * Ian Foote - [ian-foote] | ||||||
| * Chuck Harmston - [chuckharmston] | * Chuck Harmston - [chuckharmston] | ||||||
| * Philip Forget - [philipforget] | * Philip Forget - [philipforget] | ||||||
|  | * Artem Mezhenin - [amezhenin] | ||||||
| 
 | 
 | ||||||
| Many thanks to everyone who's contributed to the project. | Many thanks to everyone who's contributed to the project. | ||||||
| 
 | 
 | ||||||
|  | @ -400,3 +401,4 @@ You can also contact [@_tomchristie][twitter] directly on twitter. | ||||||
| [ian-foote]: https://github.com/ian-foote | [ian-foote]: https://github.com/ian-foote | ||||||
| [chuckharmston]: https://github.com/chuckharmston | [chuckharmston]: https://github.com/chuckharmston | ||||||
| [philipforget]: https://github.com/philipforget | [philipforget]: https://github.com/philipforget | ||||||
|  | [amezhenin]: https://github.com/amezhenin | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| import uuid | import binascii | ||||||
| import hmac | import os | ||||||
| from hashlib import sha1 | from hashlib import sha1 | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.db import models | from django.db import models | ||||||
|  | @ -34,8 +34,7 @@ class Token(models.Model): | ||||||
|         return super(Token, self).save(*args, **kwargs) |         return super(Token, self).save(*args, **kwargs) | ||||||
| 
 | 
 | ||||||
|     def generate_key(self): |     def generate_key(self): | ||||||
|         unique = uuid.uuid4() |         return binascii.hexlify(os.urandom(20)) | ||||||
|         return hmac.new(unique.bytes, digestmod=sha1).hexdigest() |  | ||||||
| 
 | 
 | ||||||
|     def __unicode__(self): |     def __unicode__(self): | ||||||
|         return self.key |         return self.key | ||||||
|  |  | ||||||
|  | @ -457,7 +457,7 @@ from django.test.client import RequestFactory as DjangoRequestFactory | ||||||
| from django.test.client import FakePayload | from django.test.client import FakePayload | ||||||
| try: | try: | ||||||
|     # In 1.5 the test client uses force_bytes |     # In 1.5 the test client uses force_bytes | ||||||
|     from django.utils.encoding import force_bytes_or_smart_bytes |     from django.utils.encoding import force_bytes as force_bytes_or_smart_bytes | ||||||
| except ImportError: | except ImportError: | ||||||
|     # In 1.3 and 1.4 the test client just uses smart_str |     # In 1.3 and 1.4 the test client just uses smart_str | ||||||
|     from django.utils.encoding import smart_str as force_bytes_or_smart_bytes |     from django.utils.encoding import smart_str as force_bytes_or_smart_bytes | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ import math | ||||||
| class APIException(Exception): | class APIException(Exception): | ||||||
|     """ |     """ | ||||||
|     Base class for REST framework exceptions. |     Base class for REST framework exceptions. | ||||||
|     Subclasses should provide `.status_code` and `.detail` properties. |     Subclasses should provide `.status_code` and `.default_detail` properties. | ||||||
|     """ |     """ | ||||||
|     status_code = status.HTTP_500_INTERNAL_SERVER_ERROR |     status_code = status.HTTP_500_INTERNAL_SERVER_ERROR | ||||||
|     default_detail = '' |     default_detail = '' | ||||||
|  |  | ||||||
|  | @ -477,6 +477,7 @@ class URLField(CharField): | ||||||
|     type_label = 'url' |     type_label = 'url' | ||||||
| 
 | 
 | ||||||
|     def __init__(self, **kwargs): |     def __init__(self, **kwargs): | ||||||
|  |         if not 'validators' in kwargs: | ||||||
|             kwargs['validators'] = [validators.URLValidator()] |             kwargs['validators'] = [validators.URLValidator()] | ||||||
|         super(URLField, self).__init__(**kwargs) |         super(URLField, self).__init__(**kwargs) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ from __future__ import unicode_literals | ||||||
| 
 | 
 | ||||||
| import copy | import copy | ||||||
| import json | import json | ||||||
|  | import django | ||||||
| from django import forms | from django import forms | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.core.exceptions import ImproperlyConfigured | ||||||
| from django.http.multipartparser import parse_header | from django.http.multipartparser import parse_header | ||||||
|  | @ -597,7 +598,7 @@ class MultiPartRenderer(BaseRenderer): | ||||||
|     media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg' |     media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg' | ||||||
|     format = 'multipart' |     format = 'multipart' | ||||||
|     charset = 'utf-8' |     charset = 'utf-8' | ||||||
|     BOUNDARY = 'BoUnDaRyStRiNg' |     BOUNDARY = 'BoUnDaRyStRiNg' if django.VERSION >= (1, 5) else b'BoUnDaRyStRiNg' | ||||||
| 
 | 
 | ||||||
|     def render(self, data, accepted_media_type=None, renderer_context=None): |     def render(self, data, accepted_media_type=None, renderer_context=None): | ||||||
|         return encode_multipart(self.BOUNDARY, data) |         return encode_multipart(self.BOUNDARY, data) | ||||||
|  |  | ||||||
|  | @ -501,7 +501,7 @@ class BaseSerializer(WritableField): | ||||||
|             else: |             else: | ||||||
|                 many = hasattr(data, '__iter__') and not isinstance(data, (Page, dict, six.text_type)) |                 many = hasattr(data, '__iter__') and not isinstance(data, (Page, dict, six.text_type)) | ||||||
|                 if many: |                 if many: | ||||||
|                     warnings.warn('Implict list/queryset serialization is deprecated. ' |                     warnings.warn('Implicit list/queryset serialization is deprecated. ' | ||||||
|                                   'Use the `many=True` flag when instantiating the serializer.', |                                   'Use the `many=True` flag when instantiating the serializer.', | ||||||
|                                   DeprecationWarning, stacklevel=3) |                                   DeprecationWarning, stacklevel=3) | ||||||
| 
 | 
 | ||||||
|  | @ -563,7 +563,7 @@ class BaseSerializer(WritableField): | ||||||
|             else: |             else: | ||||||
|                 many = hasattr(obj, '__iter__') and not isinstance(obj, (Page, dict)) |                 many = hasattr(obj, '__iter__') and not isinstance(obj, (Page, dict)) | ||||||
|                 if many: |                 if many: | ||||||
|                     warnings.warn('Implict list/queryset serialization is deprecated. ' |                     warnings.warn('Implicit list/queryset serialization is deprecated. ' | ||||||
|                                   'Use the `many=True` flag when instantiating the serializer.', |                                   'Use the `many=True` flag when instantiating the serializer.', | ||||||
|                                   DeprecationWarning, stacklevel=2) |                                   DeprecationWarning, stacklevel=2) | ||||||
| 
 | 
 | ||||||
|  | @ -893,6 +893,7 @@ class ModelSerializer(Serializer): | ||||||
|             field_name = field.source or field_name |             field_name = field.source or field_name | ||||||
|             if field_name in exclusions \ |             if field_name in exclusions \ | ||||||
|                 and not field.read_only \ |                 and not field.read_only \ | ||||||
|  |                 and field.required \ | ||||||
|                 and not isinstance(field, Serializer): |                 and not isinstance(field, Serializer): | ||||||
|                 exclusions.remove(field_name) |                 exclusions.remove(field_name) | ||||||
|         return exclusions |         return exclusions | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ from django.utils.encoding import iri_to_uri | ||||||
| from django.utils.html import escape | from django.utils.html import escape | ||||||
| from django.utils.safestring import SafeData, mark_safe | from django.utils.safestring import SafeData, mark_safe | ||||||
| from rest_framework.compat import urlparse, force_text, six, smart_urlquote | from rest_framework.compat import urlparse, force_text, six, smart_urlquote | ||||||
| import re, string | import re | ||||||
| 
 | 
 | ||||||
| register = template.Library() | register = template.Library() | ||||||
| 
 | 
 | ||||||
|  | @ -189,6 +189,17 @@ simple_url_2_re = re.compile(r'^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net | ||||||
| simple_email_re = re.compile(r'^\S+@\S+\.\S+$') | simple_email_re = re.compile(r'^\S+@\S+\.\S+$') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def smart_urlquote_wrapper(matched_url): | ||||||
|  |     """ | ||||||
|  |     Simple wrapper for smart_urlquote. ValueError("Invalid IPv6 URL") can | ||||||
|  |     be raised here, see issue #1386 | ||||||
|  |     """ | ||||||
|  |     try: | ||||||
|  |         return smart_urlquote(matched_url) | ||||||
|  |     except ValueError: | ||||||
|  |         return None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @register.filter | @register.filter | ||||||
| def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True): | def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=True): | ||||||
|     """ |     """ | ||||||
|  | @ -211,7 +222,6 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru | ||||||
|     safe_input = isinstance(text, SafeData) |     safe_input = isinstance(text, SafeData) | ||||||
|     words = word_split_re.split(force_text(text)) |     words = word_split_re.split(force_text(text)) | ||||||
|     for i, word in enumerate(words): |     for i, word in enumerate(words): | ||||||
|         match = None |  | ||||||
|         if '.' in word or '@' in word or ':' in word: |         if '.' in word or '@' in word or ':' in word: | ||||||
|             # Deal with punctuation. |             # Deal with punctuation. | ||||||
|             lead, middle, trail = '', word, '' |             lead, middle, trail = '', word, '' | ||||||
|  | @ -233,9 +243,9 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru | ||||||
|             url = None |             url = None | ||||||
|             nofollow_attr = ' rel="nofollow"' if nofollow else '' |             nofollow_attr = ' rel="nofollow"' if nofollow else '' | ||||||
|             if simple_url_re.match(middle): |             if simple_url_re.match(middle): | ||||||
|                 url = smart_urlquote(middle) |                 url = smart_urlquote_wrapper(middle) | ||||||
|             elif simple_url_2_re.match(middle): |             elif simple_url_2_re.match(middle): | ||||||
|                 url = smart_urlquote('http://%s' % middle) |                 url = smart_urlquote_wrapper('http://%s' % middle) | ||||||
|             elif not ':' in middle and simple_email_re.match(middle): |             elif not ':' in middle and simple_email_re.match(middle): | ||||||
|                 local, domain = middle.rsplit('@', 1) |                 local, domain = middle.rsplit('@', 1) | ||||||
|                 try: |                 try: | ||||||
|  |  | ||||||
|  | @ -860,7 +860,9 @@ class SlugFieldTests(TestCase): | ||||||
| 
 | 
 | ||||||
| class URLFieldTests(TestCase): | class URLFieldTests(TestCase): | ||||||
|     """ |     """ | ||||||
|     Tests for URLField attribute values |     Tests for URLField attribute values. | ||||||
|  | 
 | ||||||
|  |     (Includes test for #1210, checking that validators can be overridden.) | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     class URLFieldModel(RESTFrameworkModel): |     class URLFieldModel(RESTFrameworkModel): | ||||||
|  | @ -902,6 +904,11 @@ class URLFieldTests(TestCase): | ||||||
|         self.assertEqual(getattr(serializer.fields['url_field'], |         self.assertEqual(getattr(serializer.fields['url_field'], | ||||||
|                          'max_length'), 20) |                          'max_length'), 20) | ||||||
| 
 | 
 | ||||||
|  |     def test_validators_can_be_overridden(self): | ||||||
|  |         url_field = serializers.URLField(validators=[]) | ||||||
|  |         validators = url_field.validators | ||||||
|  |         self.assertEqual([], validators, 'Passing `validators` kwarg should have overridden default validators') | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class FieldMetadata(TestCase): | class FieldMetadata(TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|  |  | ||||||
|  | @ -91,6 +91,15 @@ class ActionItemSerializer(serializers.ModelSerializer): | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = ActionItem |         model = ActionItem | ||||||
| 
 | 
 | ||||||
|  | class ActionItemSerializerOptionalFields(serializers.ModelSerializer): | ||||||
|  |     """ | ||||||
|  |     Intended to test that fields with `required=False` are excluded from validation. | ||||||
|  |     """ | ||||||
|  |     title = serializers.CharField(required=False) | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         model = ActionItem | ||||||
|  |         fields = ('title',) | ||||||
| 
 | 
 | ||||||
| class ActionItemSerializerCustomRestore(serializers.ModelSerializer): | class ActionItemSerializerCustomRestore(serializers.ModelSerializer): | ||||||
| 
 | 
 | ||||||
|  | @ -308,7 +317,13 @@ class BasicTests(TestCase): | ||||||
|         serializer.save() |         serializer.save() | ||||||
|         self.assertIsNotNone(serializer.data.get('id',None), 'Model is saved. `id` should be set.') |         self.assertIsNotNone(serializer.data.get('id',None), 'Model is saved. `id` should be set.') | ||||||
| 
 | 
 | ||||||
| 
 |     def test_fields_marked_as_not_required_are_excluded_from_validation(self): | ||||||
|  |         """ | ||||||
|  |         Check that fields with `required=False` are included in list of exclusions. | ||||||
|  |         """ | ||||||
|  |         serializer = ActionItemSerializerOptionalFields(self.actionitem) | ||||||
|  |         exclusions = serializer.get_validation_exclusions() | ||||||
|  |         self.assertTrue('title' in exclusions, '`title` field was marked `required=False` and should be excluded') | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DictStyleSerializer(serializers.Serializer): | class DictStyleSerializer(serializers.Serializer): | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from rest_framework.test import APIRequestFactory | from rest_framework.test import APIRequestFactory | ||||||
| from rest_framework.templatetags.rest_framework import add_query_param | from rest_framework.templatetags.rest_framework import add_query_param, urlize_quoted_links | ||||||
| 
 | 
 | ||||||
| factory = APIRequestFactory() | factory = APIRequestFactory() | ||||||
| 
 | 
 | ||||||
|  | @ -17,3 +17,35 @@ class TemplateTagTests(TestCase): | ||||||
|         json_url = add_query_param(request, "format", "json") |         json_url = add_query_param(request, "format", "json") | ||||||
|         self.assertIn("q=%E6%9F%A5%E8%AF%A2", json_url) |         self.assertIn("q=%E6%9F%A5%E8%AF%A2", json_url) | ||||||
|         self.assertIn("format=json", json_url) |         self.assertIn("format=json", json_url) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Issue1386Tests(TestCase): | ||||||
|  |     """ | ||||||
|  |     Covers #1386 | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     def test_issue_1386(self): | ||||||
|  |         """ | ||||||
|  |         Test function urlize_quoted_links with different args | ||||||
|  |         """ | ||||||
|  |         correct_urls = [ | ||||||
|  |             "asdf.com", | ||||||
|  |             "asdf.net", | ||||||
|  |             "www.as_df.org", | ||||||
|  |             "as.d8f.ghj8.gov", | ||||||
|  |         ] | ||||||
|  |         for i in correct_urls: | ||||||
|  |             res = urlize_quoted_links(i) | ||||||
|  |             self.assertNotEqual(res, i) | ||||||
|  |             self.assertIn(i, res) | ||||||
|  | 
 | ||||||
|  |         incorrect_urls = [ | ||||||
|  |             "mailto://asdf@fdf.com", | ||||||
|  |             "asdf.netnet", | ||||||
|  |         ] | ||||||
|  |         for i in incorrect_urls: | ||||||
|  |             res = urlize_quoted_links(i) | ||||||
|  |             self.assertEqual(i, res) | ||||||
|  | 
 | ||||||
|  |         # example from issue #1386, this shouldn't raise an exception | ||||||
|  |         _ = urlize_quoted_links("asdf:[/p]zxcv.com") | ||||||
|  |  | ||||||
|  | @ -1,6 +1,8 @@ | ||||||
| # -- coding: utf-8 -- | # -- coding: utf-8 -- | ||||||
| 
 | 
 | ||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
|  | from io import BytesIO | ||||||
|  | 
 | ||||||
| from django.contrib.auth.models import User | from django.contrib.auth.models import User | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from rest_framework.compat import patterns, url | from rest_framework.compat import patterns, url | ||||||
|  | @ -143,3 +145,10 @@ class TestAPIRequestFactory(TestCase): | ||||||
|         force_authenticate(request, user=user) |         force_authenticate(request, user=user) | ||||||
|         response = view(request) |         response = view(request) | ||||||
|         self.assertEqual(response.data['user'], 'example') |         self.assertEqual(response.data['user'], 'example') | ||||||
|  | 
 | ||||||
|  |     def test_upload_file(self): | ||||||
|  |         # This is a 1x1 black png | ||||||
|  |         simple_png = BytesIO(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc````\x00\x00\x00\x05\x00\x01\xa5\xf6E@\x00\x00\x00\x00IEND\xaeB`\x82') | ||||||
|  |         simple_png.name = 'test.png' | ||||||
|  |         factory = APIRequestFactory() | ||||||
|  |         factory.post('/', data={'image': simple_png}) | ||||||
|  |  | ||||||
|  | @ -112,12 +112,13 @@ class APIView(View): | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def default_response_headers(self): |     def default_response_headers(self): | ||||||
|         # TODO: deprecate? |         headers = { | ||||||
|         # TODO: Only vary by accept if multiple renderers |  | ||||||
|         return { |  | ||||||
|             'Allow': ', '.join(self.allowed_methods), |             'Allow': ', '.join(self.allowed_methods), | ||||||
|             'Vary': 'Accept' |  | ||||||
|         } |         } | ||||||
|  |         if len(self.renderer_classes) > 1: | ||||||
|  |             headers['Vary'] = 'Accept' | ||||||
|  |         return headers | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     def http_method_not_allowed(self, request, *args, **kwargs): |     def http_method_not_allowed(self, request, *args, **kwargs): | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user