1919from dandiapi .api .services .exceptions import DandiError
2020from dandiapi .api .services .permissions .dandiset import get_visible_dandisets , is_dandiset_owner
2121from dandiapi .api .views .pagination import DandiPagination
22+ from dandiapi .api .views .serializers import DandisetIdentifierField
2223from dandiapi .zarr .models import ZarrArchive , ZarrArchiveStatus
2324from dandiapi .zarr .tasks import ingest_zarr_archive
2425
2526if TYPE_CHECKING :
27+ from django .contrib .auth .models import AnonymousUser , User
2628 from django .db .models .query import QuerySet
2729
2830logger = logging .getLogger (__name__ )
@@ -37,7 +39,7 @@ class ZarrDeleteFileRequestSerializer(serializers.Serializer):
3739 path = serializers .CharField ()
3840
3941
40- class ZarrSerializer (serializers .ModelSerializer ):
42+ class ZarrArchiveSerializer (serializers .ModelSerializer ):
4143 class Meta :
4244 model = ZarrArchive
4345 read_only_fields = [
@@ -49,7 +51,35 @@ class Meta:
4951 ]
5052 fields = ['name' , 'dandiset' , * read_only_fields ]
5153
52- dandiset = serializers .RegexField (f'^{ Dandiset .IDENTIFIER_REGEX } $' )
54+ dandiset = serializers .PrimaryKeyRelatedField (
55+ queryset = Dandiset .objects .all (),
56+ pk_field = DandisetIdentifierField (),
57+ )
58+
59+ def __init__ (self , instance = None , data = serializers .empty , ** kwargs ):
60+ super ().__init__ (instance = instance , data = data , ** kwargs )
61+
62+ # If this was loaded via ".get_serializer", then the request is available in context
63+ self .user : User | AnonymousUser | None = (
64+ self ._context ['request' ].user if 'request' in self ._context else None
65+ )
66+
67+ # A user is only necessary for validating input, but not to serialize output
68+ if self .user is not None :
69+ # Set the queryset here, before it's actually evaluated
70+ self .fields ['dandiset' ].queryset = get_visible_dandisets (self .user )
71+
72+ def validate_dandiset (self , dandiset : Dandiset ) -> Dandiset :
73+ if self .user is None :
74+ raise ValueError ('Serializer user not set' )
75+ if not is_dandiset_owner (dandiset , self .user ):
76+ raise PermissionDenied
77+ if dandiset .unembargo_in_progress :
78+ raise DandiError (
79+ message = 'Cannot add zarr to dandiset during unembargo' ,
80+ http_status_code = status .HTTP_400_BAD_REQUEST ,
81+ )
82+ return dandiset
5383
5484
5585class ZarrListSerializer (serializers .ModelSerializer ):
@@ -91,7 +121,7 @@ class ZarrListQuerySerializer(serializers.Serializer):
91121
92122
93123class ZarrViewSet (ReadOnlyModelViewSet ):
94- serializer_class = ZarrSerializer
124+ serializer_class = ZarrArchiveSerializer
95125 pagination_class = DandiPagination
96126
97127 queryset = ZarrArchive .objects .select_related ('dandiset' ).order_by ('created' ).all ()
@@ -127,42 +157,28 @@ def list(self, request, *args, **kwargs):
127157 return self .get_paginated_response (serializer .data )
128158
129159 @swagger_auto_schema (
130- request_body = ZarrSerializer ,
131- responses = {200 : ZarrSerializer },
160+ request_body = ZarrArchiveSerializer ,
161+ responses = {200 : ZarrArchiveSerializer },
132162 operation_summary = 'Create a new zarr archive.' ,
133163 operation_description = '' ,
134164 )
135165 def create (self , request ):
136166 """Create a new zarr archive."""
137- serializer = ZarrSerializer (data = request .data )
167+ serializer = self . get_serializer (data = request .data )
138168 serializer .is_valid (raise_exception = True )
139169
140- name = serializer .validated_data ['name' ]
141- dandiset = get_object_or_404 (
142- get_visible_dandisets (request .user ), id = serializer .validated_data ['dandiset' ]
143- )
144- if not is_dandiset_owner (dandiset , request .user ):
145- raise PermissionDenied
146-
147- # Prevent addition to dandiset during unembargo
148- if dandiset .unembargo_in_progress :
149- raise DandiError (
150- message = 'Cannot add zarr to dandiset during unembargo' ,
151- http_status_code = status .HTTP_400_BAD_REQUEST ,
152- )
153-
154- zarr_archive : ZarrArchive = ZarrArchive (name = name , dandiset = dandiset )
155170 with transaction .atomic ():
156- # Use nested transaction block to prevent zarr creation race condition
157171 try :
158- with transaction .atomic ():
159- zarr_archive .save ()
172+ zarr_archive : ZarrArchive = serializer .save ()
160173 except IntegrityError as e :
161174 raise ValidationError ('Zarr already exists' ) from e
162175
163- audit .create_zarr (dandiset = dandiset , user = request .user , zarr_archive = zarr_archive )
176+ audit .create_zarr (
177+ dandiset = serializer .validated_data ['dandiset' ],
178+ user = request .user ,
179+ zarr_archive = zarr_archive ,
180+ )
164181
165- serializer = ZarrSerializer (instance = zarr_archive )
166182 return Response (serializer .data , status = status .HTTP_200_OK )
167183
168184 @swagger_auto_schema (
@@ -328,7 +344,7 @@ def create_files(self, request, zarr_id):
328344 @swagger_auto_schema (
329345 request_body = ZarrDeleteFileRequestSerializer (many = True ),
330346 responses = {
331- 200 : ZarrSerializer (many = True ),
347+ 200 : ZarrArchiveSerializer (many = True ),
332348 400 : ZarrArchive .INGEST_ERROR_MSG ,
333349 },
334350 operation_summary = 'Delete files from a zarr archive.' ,
0 commit comments