api: handle multiple objects returned in get-or-create mixin (#44301)

This commit is contained in:
Paul Marillonnet 2020-07-30 17:31:00 +02:00 committed by Benjamin Dauvergne
parent d305ddd6f6
commit bf5df42709
2 changed files with 47 additions and 0 deletions

View File

@ -15,12 +15,21 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from rest_framework import status
from rest_framework.settings import api_settings
from rest_framework.exceptions import APIException
from rest_framework.exceptions import ValidationError
from rest_framework.utils import model_meta
class Conflict(APIException):
status_code = status.HTTP_409_CONFLICT
default_detail = _('Cannot process request because of conflicting resources.')
default_code = 'conflict'
class GetOrCreateMixinView(object):
_lookup_object = None
@ -49,6 +58,9 @@ class GetOrCreateMixinView(object):
return ModelClass.objects.get(**kwargs)
except ModelClass.DoesNotExist:
return None
except ModelClass.MultipleObjectsReturned:
raise Conflict('retrieved several instances of model %s for key attributes %s' % (
ModelClass.__name__, kwargs))
def _validate_get_keys(self, keys):
ModelClass = self._get_model_class()

View File

@ -1507,6 +1507,41 @@ def test_api_users_get_or_create_email_is_unique(settings, app, admin):
assert User.objects.get(id=id).last_name == 'Doe'
def test_api_users_get_or_create_email_not_unique(settings, app, admin):
settings.A2_EMAIL_IS_UNIQUE = False
User = get_user_model()
OU = get_ou_model()
ou1 = OU.objects.create(name='OU1', slug='ou1', email_is_unique=True)
ou2 = OU.objects.create(name='OU2', slug='ou2', email_is_unique=False)
app.authorization = ('Basic', (admin.username, admin.username))
payload = {
'email': 'john.doe@example.net',
'first_name': 'John',
'last_name': 'Doe',
'ou': 'ou1'
}
# 1. create
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=201)
id_user = resp.json['id']
assert User.objects.get(id=id_user).first_name == 'John'
assert User.objects.get(id=id_user).last_name == 'Doe'
assert User.objects.get(id=id_user).ou == ou1
# 2. get
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=200)
# 3. explicitly create in a different OU
payload['ou'] = 'ou2'
resp = app.post_json('/api/users/', params=payload, status=201)
id_user2 = resp.json['id']
assert id_user2 != id_user
assert User.objects.get(id=id_user2).first_name == 'John'
assert User.objects.get(id=id_user2).last_name == 'Doe'
assert User.objects.get(id=id_user2).ou == ou2
# 4. fail to retrieve a single instance for an ambiguous get-or-create key
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=409)
assert resp.json['errors'] == "retrieved several instances of model User for key attributes {'email': 'john.doe@example.net'}"
def test_api_users_get_or_create_multi_key(settings, app, admin):
app.authorization = ('Basic', (admin.username, admin.username))
# test missing first_name