From 739d3892c5e948264614eae719c0fd71645b936c Mon Sep 17 00:00:00 2001 From: Nic Young Date: Wed, 7 Oct 2015 22:20:46 -0700 Subject: [PATCH 01/12] Fix codecov on Travis CI The coverage report needs to be explicitly created with arguments passed in to pytest-cov --- .travis.yml | 2 +- runtests.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e8561a7ac..67404d750 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,4 +31,4 @@ script: after_success: - pip install codecov - - codecov + - codecov -e TOX_ENV diff --git a/runtests.py b/runtests.py index 36a2183bd..504cd1d37 100755 --- a/runtests.py +++ b/runtests.py @@ -93,7 +93,11 @@ if __name__ == "__main__": except ValueError: pass else: - pytest_args = ['--cov', 'rest_framework'] + pytest_args + pytest_args = [ + '--cov-report', + 'xml', + '--cov', + 'rest_framework'] + pytest_args if first_arg.startswith('-'): # `runtests.py [flags]` From 431ac45168e130a7f8b88549f2662cde6b455877 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Fri, 9 Oct 2015 11:03:19 +0800 Subject: [PATCH 02/12] Update compat.py try to import guardian if it's in INSTALLED_APPS --- rest_framework/compat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 5ca5da20e..1ef3e1751 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -81,8 +81,9 @@ except ImportError: # Fixes (#1712). We keep the try/except for the test suite. guardian = None try: - import guardian - import guardian.shortcuts # Fixes #1624 + if 'guardian' in settings.INSTALLED_APPS: + import guardian + import guardian.shortcuts # Fixes #1624 except ImportError: pass From 47c9d2a4d38587ba2d30b9fbfe0384b819c64ebd Mon Sep 17 00:00:00 2001 From: demokrates Date: Mon, 12 Oct 2015 10:10:08 +0200 Subject: [PATCH 03/12] Update versioning.md Changed settings attribute 'VERSION_PARAMETER' --> to 'VERSION_PARAM'. --- docs/api-guide/versioning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/versioning.md b/docs/api-guide/versioning.md index 482943b86..06b0056a4 100644 --- a/docs/api-guide/versioning.md +++ b/docs/api-guide/versioning.md @@ -72,7 +72,7 @@ The following settings keys are also used to control versioning: * `DEFAULT_VERSION`. The value that should be used for `request.version` when no versioning information is present. Defaults to `None`. * `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version if not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set. Defaults to `None`. -* `VERSION_PARAMETER`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`. +* `VERSION_PARAM`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`. You can also set your versioning class plus those three values on a per-view or a per-viewset basis by defining your own versioning scheme and using the `default_version`, `allowed_versions` and `version_param` class variables. For example, if you want to use `URLPathVersioning`: From 79008ea2106353f58d0fea4196f9ac614db74426 Mon Sep 17 00:00:00 2001 From: Aider Ibragimov Date: Wed, 14 Oct 2015 15:56:26 +0300 Subject: [PATCH 04/12] fix typo for adding jquery string in AdminRenderer --- rest_framework/templates/rest_framework/admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/templates/rest_framework/admin.html b/rest_framework/templates/rest_framework/admin.html index e1751b21c..318a1e706 100644 --- a/rest_framework/templates/rest_framework/admin.html +++ b/rest_framework/templates/rest_framework/admin.html @@ -219,7 +219,7 @@ {% endif %} {% block script %} - + From 6fe021eea7a917bc57ebdc11e5db02c1cbccddb6 Mon Sep 17 00:00:00 2001 From: Zack Tanner Date: Wed, 14 Oct 2015 19:09:11 -0700 Subject: [PATCH 05/12] Various typo fixes --- docs/api-guide/serializers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 63189b966..95703033a 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -287,7 +287,7 @@ Similarly if a nested representation should be a list of items, you should pass ## Writable nested representations -When dealing with nested representations that support deserializing the data, an errors with nested objects will be nested under the field name of the nested object. +When dealing with nested representations that support deserializing the data, any errors with nested objects will be nested under the field name of the nested object. serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) serializer.is_valid() @@ -356,7 +356,7 @@ It is possible that a third party package, providing automatic support some kind #### Handling saving related instances in model manager classes -An alternative to saving multiple related instances in the serializer is to write custom model manager classes handle creating the correct instances. +An alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances. For example, suppose we wanted to ensure that `User` instances and `Profile` instances are always created together as a pair. We might write a custom manager class that looks something like this: @@ -478,7 +478,7 @@ For example: model = Account fields = '__all__' -You can set the `exclude` attribute of the to a list of fields to be excluded from the serializer. +You can set the `exclude` attribute to a list of fields to be excluded from the serializer. For example: @@ -551,7 +551,7 @@ Please review the [Validators Documentation](/api-guide/validators/) for details ## Additional keyword arguments -There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer. +There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. As in the case of `read_only_fields`, this means you do not need to explicitly declare the field on the serializer. This option is a dictionary, mapping field names to a dictionary of keyword arguments. For example: @@ -832,7 +832,7 @@ This class implements the same basic API as the `Serializer` class: * `.data` - Returns the outgoing primitive representation. * `.is_valid()` - Deserializes and validates incoming data. * `.validated_data` - Returns the validated incoming data. -* `.errors` - Returns an errors during validation. +* `.errors` - Returns any errors during validation. * `.save()` - Persists the validated data into an object instance. There are four methods that can be overridden, depending on what functionality you want the serializer class to support: From 392df94693edf84be7697da3eca512a384e7b77c Mon Sep 17 00:00:00 2001 From: auvipy Date: Thu, 15 Oct 2015 14:16:35 +0600 Subject: [PATCH 06/12] removed south migrations --- .../south_migrations/0001_initial.py | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/rest_framework/authtoken/south_migrations/0001_initial.py b/rest_framework/authtoken/south_migrations/0001_initial.py index 5b927f3e5..8b1378917 100644 --- a/rest_framework/authtoken/south_migrations/0001_initial.py +++ b/rest_framework/authtoken/south_migrations/0001_initial.py @@ -1,60 +1 @@ -# -*- coding: utf-8 -*- -from south.db import db -from south.v2 import SchemaMigration -try: - from django.contrib.auth import get_user_model -except ImportError: # django < 1.5 - from django.contrib.auth.models import User -else: - User = get_user_model() - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'Token' - db.create_table('authtoken_token', ( - ('key', self.gf('django.db.models.fields.CharField')(max_length=40, primary_key=True)), - ('user', self.gf('django.db.models.fields.related.OneToOneField')(related_name='auth_token', unique=True, to=orm['%s.%s' % (User._meta.app_label, User._meta.object_name)])), - ('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), - )) - db.send_create_signal('authtoken', ['Token']) - - def backwards(self, orm): - # Deleting model 'Token' - db.delete_table('authtoken_token') - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - "%s.%s" % (User._meta.app_label, User._meta.module_name): { - 'Meta': {'object_name': User._meta.module_name, 'db_table': repr(User._meta.db_table)}, - }, - 'authtoken.token': { - 'Meta': {'object_name': 'Token'}, - 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'auth_token'", 'unique': 'True', 'to': "orm['%s.%s']" % (User._meta.app_label, User._meta.object_name)}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - } - } - - complete_apps = ['authtoken'] From 4db25013999b7808ae0a8fcec29fb9ea3e0a03f4 Mon Sep 17 00:00:00 2001 From: auvipy Date: Thu, 15 Oct 2015 14:17:56 +0600 Subject: [PATCH 07/12] removed south_migrations directory --- rest_framework/authtoken/south_migrations/0001_initial.py | 1 - rest_framework/authtoken/south_migrations/__init__.py | 0 2 files changed, 1 deletion(-) delete mode 100644 rest_framework/authtoken/south_migrations/0001_initial.py delete mode 100644 rest_framework/authtoken/south_migrations/__init__.py diff --git a/rest_framework/authtoken/south_migrations/0001_initial.py b/rest_framework/authtoken/south_migrations/0001_initial.py deleted file mode 100644 index 8b1378917..000000000 --- a/rest_framework/authtoken/south_migrations/0001_initial.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rest_framework/authtoken/south_migrations/__init__.py b/rest_framework/authtoken/south_migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 From a1dad503cfe637e9575168f74782eca654dbb041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Padilla?= Date: Fri, 16 Oct 2015 07:17:33 -0400 Subject: [PATCH 08/12] Map TextField max_length to CharField --- rest_framework/utils/field_mapping.py | 3 ++- tests/test_model_serializer.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 8206578ee..f109ad80d 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -123,7 +123,8 @@ def get_field_kwargs(field_name, model_field): # Ensure that max_length is passed explicitly as a keyword arg, # rather than as a validator. max_length = getattr(model_field, 'max_length', None) - if max_length is not None and isinstance(model_field, models.CharField): + if max_length is not None and (isinstance(model_field, models.CharField) or + isinstance(model_field, models.TextField)): kwargs['max_length'] = max_length validator_kwarg = [ validator for validator in validator_kwarg diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index aa62ec4ae..c9b2d313e 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -63,7 +63,7 @@ class RegularFieldsModel(models.Model): positive_small_integer_field = models.PositiveSmallIntegerField() slug_field = models.SlugField(max_length=100) small_integer_field = models.SmallIntegerField() - text_field = models.TextField() + text_field = models.TextField(max_length=100) time_field = models.TimeField() url_field = models.URLField(max_length=100) custom_field = CustomField() @@ -161,11 +161,12 @@ class TestRegularFieldMappings(TestCase): positive_small_integer_field = IntegerField() slug_field = SlugField(max_length=100) small_integer_field = IntegerField() - text_field = CharField(style={'base_template': 'textarea.html'}) + text_field = CharField(max_length=100, style={'base_template': 'textarea.html'}) time_field = TimeField() url_field = URLField(max_length=100) custom_field = ModelField(model_field=) """) + self.assertEqual(unicode_repr(TestSerializer()), expected) def test_field_options(self): From dc72fb4746be0a6dfc66fd5c4de82d0b016fba34 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 19:43:00 +0200 Subject: [PATCH 09/12] Missing the `source=` keyword for the URLField parameter --- docs/api-guide/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index f8be0c1b9..377fe983f 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -57,7 +57,7 @@ Note that setting a `default` value implies that the field is not required. Incl ### `source` -The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField('get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. +The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `URLField(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `EmailField(source='user.email')`. The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation. From 2e178bc970f063a829e26d3b5ec060ef66e87933 Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Sat, 17 Oct 2015 12:00:11 +0300 Subject: [PATCH 10/12] Replaced all dict and set conversions from lists to dict and set literals. --- rest_framework/exceptions.py | 6 +++--- rest_framework/fields.py | 36 +++++++++++++++++------------------ rest_framework/metadata.py | 2 +- rest_framework/pagination.py | 6 +----- rest_framework/routers.py | 2 +- rest_framework/serializers.py | 25 +++++++++++------------- rest_framework/validators.py | 18 +++++++++--------- 7 files changed, 44 insertions(+), 51 deletions(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 236087bde..8447a9ded 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -30,10 +30,10 @@ def _force_text_recursive(data): return ReturnList(ret, serializer=data.serializer) return data elif isinstance(data, dict): - ret = dict([ - (key, _force_text_recursive(value)) + ret = { + key: _force_text_recursive(value) for key, value in data.items() - ]) + } if isinstance(data, ReturnDict): return ReturnDict(ret, serializer=data.serializer) return data diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 0d4a51152..0b214b872 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -604,8 +604,8 @@ class BooleanField(Field): } default_empty_html = False initial = False - TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) - FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False)) + TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True} + FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False} def __init__(self, **kwargs): assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.' @@ -634,9 +634,9 @@ class NullBooleanField(Field): 'invalid': _('"{input}" is not a valid boolean.') } initial = None - TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True)) - FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False)) - NULL_VALUES = set(('n', 'N', 'null', 'Null', 'NULL', '', None)) + TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True} + FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False} + NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None} def __init__(self, **kwargs): assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.' @@ -1241,9 +1241,9 @@ class ChoiceField(Field): # Map the string representation of choices to the underlying value. # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. - self.choice_strings_to_values = dict([ - (six.text_type(key), key) for key in self.choices.keys() - ]) + self.choice_strings_to_values = { + six.text_type(key): key for key in self.choices.keys() + } self.allow_blank = kwargs.pop('allow_blank', False) @@ -1302,15 +1302,15 @@ class MultipleChoiceField(ChoiceField): if not self.allow_empty and len(data) == 0: self.fail('empty') - return set([ + return { super(MultipleChoiceField, self).to_internal_value(item) for item in data - ]) + } def to_representation(self, value): - return set([ + return { self.choice_strings_to_values.get(six.text_type(item), item) for item in value - ]) + } class FilePathField(ChoiceField): @@ -1508,19 +1508,19 @@ class DictField(Field): data = html.parse_html_dict(data) if not isinstance(data, dict): self.fail('not_a_dict', input_type=type(data).__name__) - return dict([ - (six.text_type(key), self.child.run_validation(value)) + return { + six.text_type(key): self.child.run_validation(value) for key, value in data.items() - ]) + } def to_representation(self, value): """ List of object instances -> List of dicts of primitive datatypes. """ - return dict([ - (six.text_type(key), self.child.to_representation(val)) + return { + six.text_type(key): self.child.to_representation(val) for key, val in value.items() - ]) + } class JSONField(Field): diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index aba1a2013..6c4f17692 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -77,7 +77,7 @@ class SimpleMetadata(BaseMetadata): the fields that are accepted for 'PUT' and 'POST' methods. """ actions = {} - for method in set(['PUT', 'POST']) & set(view.allowed_methods): + for method in {'PUT', 'POST'} & set(view.allowed_methods): view.request = clone_request(request, method) try: # Test global permissions diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index d82a755c8..45b5b29c5 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -79,11 +79,7 @@ def _get_displayed_page_numbers(current, final): # We always include the first two pages, last two pages, and # two pages either side of the current page. - included = set(( - 1, - current - 1, current, current + 1, - final - )) + included = {1, current - 1, current, current + 1, final} # If the break would only exclude a single page number then we # may as well include the page number instead of the break. diff --git a/rest_framework/routers.py b/rest_framework/routers.py index d4e9d95ed..39605923d 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -174,7 +174,7 @@ class SimpleRouter(BaseRouter): url_path = initkwargs.pop("url_path", None) or methodname ret.append(Route( url=replace_methodname(route.url, url_path), - mapping=dict((httpmethod, methodname) for httpmethod in httpmethods), + mapping={httpmethod: methodname for httpmethod in httpmethods}, name=replace_methodname(route.name, url_path), initkwargs=initkwargs, )) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 5aef1df69..d505d8d3a 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -125,10 +125,10 @@ class BaseSerializer(Field): } if allow_empty is not None: list_kwargs['allow_empty'] = allow_empty - list_kwargs.update(dict([ - (key, value) for key, value in kwargs.items() + list_kwargs.update({ + key: value for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS - ])) + }) meta = getattr(cls, 'Meta', None) list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer) return list_serializer_class(*args, **list_kwargs) @@ -305,10 +305,10 @@ def get_validation_error_detail(exc): elif isinstance(exc.detail, dict): # If errors may be a dict we use the standard {key: list of values}. # Here we ensure that all the values are *lists* of errors. - return dict([ - (key, value if isinstance(value, list) else [value]) + return { + key: value if isinstance(value, list) else [value] for key, value in exc.detail.items() - ]) + } elif isinstance(exc.detail, list): # Errors raised as a list are non-field errors. return { @@ -1237,13 +1237,10 @@ class ModelSerializer(Serializer): for model_field in model_fields.values(): # Include each of the `unique_for_*` field names. - unique_constraint_names |= set([ - model_field.unique_for_date, - model_field.unique_for_month, - model_field.unique_for_year - ]) + unique_constraint_names |= {model_field.unique_for_date, model_field.unique_for_month, + model_field.unique_for_year} - unique_constraint_names -= set([None]) + unique_constraint_names -= {None} # Include each of the `unique_together` field names, # so long as all the field names are included on the serializer. @@ -1357,10 +1354,10 @@ class ModelSerializer(Serializer): # which may map onto a model field. Any dotted field name lookups # cannot map to a field, and must be a traversal, so we're not # including those. - field_names = set([ + field_names = { field.source for field in self.fields.values() if (field.source != '*') and ('.' not in field.source) - ]) + } # Note that we make sure to check `unique_together` both on the # base model class, but also on any parent classes. diff --git a/rest_framework/validators.py b/rest_framework/validators.py index a1771a92e..a21f67e60 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -100,11 +100,11 @@ class UniqueTogetherValidator(object): if self.instance is not None: return - missing = dict([ - (field_name, self.missing_message) + missing = { + field_name: self.missing_message for field_name in self.fields if field_name not in attrs - ]) + } if missing: raise ValidationError(missing) @@ -120,10 +120,10 @@ class UniqueTogetherValidator(object): attrs[field_name] = getattr(self.instance, field_name) # Determine the filter keyword arguments and filter the queryset. - filter_kwargs = dict([ - (field_name, attrs[field_name]) + filter_kwargs = { + field_name: attrs[field_name] for field_name in self.fields - ]) + } return queryset.filter(**filter_kwargs) def exclude_current_instance(self, attrs, queryset): @@ -184,11 +184,11 @@ class BaseUniqueForValidator(object): The `UniqueForValidator` classes always force an implied 'required' state on the fields they are applied to. """ - missing = dict([ - (field_name, self.missing_message) + missing = { + field_name: self.missing_message for field_name in [self.field, self.date_field] if field_name not in attrs - ]) + } if missing: raise ValidationError(missing) From 6757da5aab1c774654b70ab21237ce5b99ce12d7 Mon Sep 17 00:00:00 2001 From: agconti Date: Sun, 18 Oct 2015 20:13:09 -0400 Subject: [PATCH 11/12] docs(third-party-resources): added cookiecutter-django-rest to misc third party resources --- docs/topics/third-party-resources.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md index bef3effc7..a4f1eefcb 100644 --- a/docs/topics/third-party-resources.md +++ b/docs/topics/third-party-resources.md @@ -237,6 +237,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque ### Misc +* [cookiecutter-django-rest][cookiecutter-django-rest] - A cookiecutter template that takes care of the setup and configuration so you can focus on making your REST apis awesome. * [djangorestrelationalhyperlink][djangorestrelationalhyperlink] - A hyperlinked serialiser that can can be used to alter relationships via hyperlinks, but otherwise like a hyperlink model serializer. * [django-rest-swagger][django-rest-swagger] - An API documentation generator for Swagger UI. * [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server. @@ -346,4 +347,5 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque [django-rest-framework-braces]: https://github.com/dealertrack/django-rest-framework-braces [dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions [django-url-filter]: https://github.com/miki725/django-url-filter +[cookiecutter-django-rest]: https://github.com/agconti/cookiecutter-django-rest [drf-haystack]: http://drf-haystack.readthedocs.org/en/latest/ From 5b5d6f1d4befbaabaff3103221addfefe9fbe77c Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 20 Oct 2015 10:53:45 +0100 Subject: [PATCH 12/12] Test against Django 1.9 beta --- tox.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 0ef64cc49..4648f592c 100644 --- a/tox.ini +++ b/tox.ini @@ -19,9 +19,9 @@ commands = ./runtests.py --fast {posargs} --coverage setenv = PYTHONDONTWRITEBYTECODE=1 deps = - django17: Django==1.7.10 # Should track maximum supported - django18: Django==1.8.4 # Should track maximum supported - django19: https://www.djangoproject.com/download/1.9a1/tarball/ + django17: Django==1.7.10 + django18: Django==1.8.4 + django19: https://www.djangoproject.com/download/1.9b1/tarball/ -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt @@ -40,21 +40,21 @@ deps = # Specify explicitly to exclude Django Guardian against Django 1.9 [testenv:py27-django19] deps = - https://www.djangoproject.com/download/1.9a1/tarball/ + https://www.djangoproject.com/download/1.9b1/tarball/ -rrequirements/requirements-testing.txt markdown==2.5.2 django-filter==0.10.0 [testenv:py34-django19] deps = - https://www.djangoproject.com/download/1.9a1/tarball/ + https://www.djangoproject.com/download/1.9b1/tarball/ -rrequirements/requirements-testing.txt markdown==2.5.2 django-filter==0.10.0 [testenv:py35-django19] deps = - https://www.djangoproject.com/download/1.9a1/tarball/ + https://www.djangoproject.com/download/1.9b1/tarball/ -rrequirements/requirements-testing.txt markdown==2.5.2 django-filter==0.10.0