From 8609c9ca8c0d9c747d594d2b4f5cc7cc8be98f0a Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 14 Sep 2016 23:44:48 -0400 Subject: [PATCH] Fix Django 1.10 to-many deprecation --- rest_framework/compat.py | 8 ++++++++ rest_framework/serializers.py | 10 +++++++--- tests/test_model_serializer.py | 4 ++-- tests/test_permissions.py | 10 +++++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index cee430a84..9ee40b257 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -277,3 +277,11 @@ def template_render(template, context=None, request=None): # backends template, e.g. django.template.backends.django.Template else: return template.render(context, request=request) + + +def set_many(instance, field, value): + if django.VERSION < (1, 10): + setattr(instance, field, value) + else: + field = getattr(instance, field) + field.set(value) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 4d1ed63ae..28f70bd40 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -23,7 +23,7 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import JSONField as ModelJSONField -from rest_framework.compat import postgres_fields, unicode_to_repr +from rest_framework.compat import postgres_fields, set_many, unicode_to_repr from rest_framework.utils import model_meta from rest_framework.utils.field_mapping import ( ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs, @@ -892,19 +892,23 @@ class ModelSerializer(Serializer): # Save many-to-many relationships after the instance is created. if many_to_many: for field_name, value in many_to_many.items(): - setattr(instance, field_name, value) + set_many(instance, field_name, value) return instance def update(self, instance, validated_data): raise_errors_on_nested_writes('update', self, validated_data) + info = model_meta.get_field_info(instance) # Simply set each attribute on the instance, and then save it. # Note that unlike `.create()` we don't need to treat many-to-many # relationships as being a special case. During updates we already # have an instance pk for the relationships to be associated with. for attr, value in validated_data.items(): - setattr(instance, attr, value) + if attr in info.relations and info.relations[attr].to_many: + set_many(instance, attr, value) + else: + setattr(instance, attr, value) instance.save() return instance diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 01243ff6e..5dac16f2b 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -20,7 +20,7 @@ from django.test import TestCase from django.utils import six from rest_framework import serializers -from rest_framework.compat import unicode_repr +from rest_framework.compat import set_many, unicode_repr def dedent(blocktext): @@ -651,7 +651,7 @@ class TestIntegration(TestCase): foreign_key=self.foreign_key_target, one_to_one=self.one_to_one_target, ) - self.instance.many_to_many = self.many_to_many_targets + set_many(self.instance, 'many_to_many', self.many_to_many_targets) self.instance.save() def test_pk_retrival(self): diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 5cef22628..0445f27ca 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -12,7 +12,7 @@ from rest_framework import ( HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, status ) -from rest_framework.compat import guardian +from rest_framework.compat import guardian, set_many from rest_framework.filters import DjangoObjectPermissionsFilter from rest_framework.routers import DefaultRouter from rest_framework.test import APIRequestFactory @@ -74,15 +74,15 @@ class ModelPermissionsIntegrationTests(TestCase): def setUp(self): User.objects.create_user('disallowed', 'disallowed@example.com', 'password') user = User.objects.create_user('permitted', 'permitted@example.com', 'password') - user.user_permissions = [ + set_many(user, 'user_permissions', [ Permission.objects.get(codename='add_basicmodel'), Permission.objects.get(codename='change_basicmodel'), Permission.objects.get(codename='delete_basicmodel') - ] + ]) user = User.objects.create_user('updateonly', 'updateonly@example.com', 'password') - user.user_permissions = [ + set_many(user, 'user_permissions', [ Permission.objects.get(codename='change_basicmodel'), - ] + ]) self.permitted_credentials = basic_auth_header('permitted', 'password') self.disallowed_credentials = basic_auth_header('disallowed', 'password')