5
5
from dataclasses import dataclass
6
6
from typing import Optional
7
7
8
+ from django .db import transaction
8
9
from django .contrib .auth .models import Permission , User
9
10
from django .urls import Resolver404
10
11
from django .utils .translation import gettext as t
@@ -326,6 +327,7 @@ class PermissionAssignment:
326
327
permission_codename : str
327
328
partial_permissions_json : Optional [str ] = None
328
329
330
+ @transaction .atomic
329
331
def create (self , validated_data ):
330
332
asset = self .context ['asset' ]
331
333
user_pk_to_obj_cache = dict ()
@@ -468,10 +470,10 @@ def validate(self, attrs):
468
470
Validate users and permissions, and convert them from API URLs into
469
471
model instances, using a minimal number of database queries
470
472
"""
471
- # A dictionary for looking up usernames by API user URLs
472
- url_to_username = dict ()
473
- # …for looking up codenames by API permission URLs
474
- url_to_codename = dict ()
473
+ # A dictionary for looking up API user URLs by username
474
+ username_to_url = dict ()
475
+ # …for looking up by API permission URLs by codename
476
+ codename_to_url = dict ()
475
477
476
478
assignable_permissions = self .context [
477
479
'asset'
@@ -497,9 +499,9 @@ def validate(self, attrs):
497
499
codename = self ._get_arg_from_url ('codename' , perm_url )
498
500
if codename not in assignable_permissions :
499
501
raise serializers .ValidationError (INVALID_PERMISSION_ERROR )
500
- url_to_codename [ perm_url ] = codename
502
+ codename_to_url [ codename ] = perm_url
501
503
username = self ._get_arg_from_url ('username' , user_url )
502
- url_to_username [ user_url ] = username
504
+ username_to_url [ username ] = user_url
503
505
for partial_assignment in assignment .get ('partial_permissions' , []):
504
506
partial_codename = self ._get_arg_from_url (
505
507
'codename' , partial_assignment ['url' ]
@@ -511,42 +513,28 @@ def validate(self, attrs):
511
513
raise serializers .ValidationError (
512
514
INVALID_PARTIAL_PERMISSION_ERROR
513
515
)
514
- url_to_codename [ partial_assignment ['url' ]] = partial_codename
516
+ codename_to_url [ partial_codename ] = partial_assignment ['url' ]
515
517
516
518
# Create a dictionary of API user URLs to `User` objects
517
- urls_sorted_by_username = [
518
- url
519
- for url , username in sorted (
520
- url_to_username .items (), key = lambda item : item [1 ]
521
- )
522
- ]
523
- url_to_user = dict (
524
- zip (
525
- urls_sorted_by_username ,
526
- User .objects .only ('pk' , 'username' )
527
- .filter (username__in = url_to_username .values ())
528
- .order_by ('username' ),
529
- )
530
- )
531
- if len (url_to_user ) != len (url_to_username ):
519
+ url_to_user = dict ()
520
+ for user in User .objects .only ('pk' , 'username' ).filter (
521
+ username__in = username_to_url .keys ()
522
+ ):
523
+ url = username_to_url [user .username ]
524
+ url_to_user [url ] = user
525
+ if len (url_to_user ) != len (username_to_url ):
532
526
raise serializers .ValidationError (INVALID_USER_ERROR )
533
527
534
528
# Create a dictionary of API permission URLs to `Permission` objects
535
- urls_sorted_by_codename = [
536
- url
537
- for url , codename in sorted (
538
- url_to_codename .items (), key = lambda item : item [1 ]
539
- )
540
- ]
541
- url_to_permission = dict (
542
- zip (
543
- urls_sorted_by_codename ,
544
- Permission .objects .filter (codename__in = assignable_permissions )
545
- .filter (codename__in = url_to_codename .values ())
546
- .order_by ('codename' ),
547
- )
548
- )
549
- if len (url_to_permission ) != len (url_to_codename ):
529
+ url_to_permission = dict ()
530
+ for permission in (
531
+ Permission .objects .filter (codename__in = assignable_permissions )
532
+ .filter (codename__in = codename_to_url .keys ())
533
+ .order_by ('codename' )
534
+ ):
535
+ url = codename_to_url [permission .codename ]
536
+ url_to_permission [url ] = permission
537
+ if len (url_to_permission ) != len (codename_to_url ):
550
538
# This should never happen since all codenames were found within
551
539
# `assignable_permissions`
552
540
raise RuntimeError (
@@ -569,11 +557,9 @@ def validate(self, attrs):
569
557
list
570
558
)
571
559
for partial_assignment in assignment ['partial_permissions' ]:
572
- # Convert partial permission URLs to codenames only; it's
573
- # unnecessary to instantiate objects for them
574
- partial_codename = url_to_codename [
560
+ partial_codename = url_to_permission [
575
561
partial_assignment ['url' ]
576
- ]
562
+ ]. codename
577
563
assignment_with_objects ['partial_permissions' ][
578
564
partial_codename
579
565
] = partial_assignment ['filters' ]
0 commit comments