class RepositorySerializer(ModelSerializer): _href = IdentityField( view_name='repositories-detail' ) _versions_href = IdentityField( view_name='versions-list', lookup_url_kwarg='repository_pk', ) _latest_version_href = LatestVersionField() name = serializers.CharField( help_text=_('A unique name for this repository.'), validators=[UniqueValidator(queryset=models.Repository.objects.all())] ) description = serializers.CharField( help_text=_('An optional description.'), required=False, allow_blank=True ) notes = GenericKeyValueRelatedField( help_text=_('A mapping of string keys to string values, for storing notes on this object.'), required=False ) class Meta: model = models.Repository fields = ModelSerializer.Meta.fields + ('_versions_href', '_latest_version_href', 'name', 'description', 'notes')
class RepositorySerializer(ModelSerializer): pulp_href = IdentityField(view_name='repositories-detail') versions_href = IdentityField( view_name='versions-list', lookup_url_kwarg='repository_pk', ) latest_version_href = LatestVersionField() name = serializers.CharField( help_text=_('A unique name for this repository.'), validators=[UniqueValidator(queryset=models.Repository.objects.all())]) plugin_managed = serializers.BooleanField( help_text= _('True if the plugin manages this repository, users typically do not interact with it.' ), read_only=True, ) description = serializers.CharField( help_text=_('An optional description.'), required=False, allow_null=True) class Meta: model = models.Repository fields = ModelSerializer.Meta.fields + ( 'versions_href', 'latest_version_href', 'name', 'plugin_managed', 'description')
class WorkerSerializer(ModelSerializer): _href = IdentityField(view_name='workers-detail') name = serializers.CharField( help_text=_('The name of the worker.'), read_only=True ) last_heartbeat = serializers.DateTimeField( help_text=_('Timestamp of the last time the worker talked to the service.'), read_only=True ) online = serializers.BooleanField( help_text=_('True if the worker is considered online, otherwise False'), read_only=True ) missing = serializers.BooleanField( help_text=_('True if the worker is considerd missing, otherwise False'), read_only=True ) # disable "created" because we don't care about it created = None class Meta: model = models.Worker _base_fields = tuple(set(ModelSerializer.Meta.fields) - set(['created'])) fields = _base_fields + ('name', 'last_heartbeat', 'online', 'missing')
class TaskGroupSerializer(ModelSerializer): pulp_href = IdentityField(view_name='task-groups-detail') description = serializers.CharField( help_text=_("A description of the task group.")) waiting = TaskGroupStatusCountField( state=TASK_STATES.WAITING, help_text=_("Number of tasks in the 'waiting' state")) skipped = TaskGroupStatusCountField( state=TASK_STATES.SKIPPED, help_text=_("Number of tasks in the 'skipped' state")) running = TaskGroupStatusCountField( state=TASK_STATES.RUNNING, help_text=_("Number of tasks in the 'running' state")) completed = TaskGroupStatusCountField( state=TASK_STATES.COMPLETED, help_text=_("Number of tasks in the 'completed' state")) canceled = TaskGroupStatusCountField( state=TASK_STATES.CANCELED, help_text=_("Number of tasks in the 'canceled' state")) failed = TaskGroupStatusCountField( state=TASK_STATES.FAILED, help_text=_("Number of tasks in the 'failed' state")) class Meta: model = models.TaskGroup fields = ('pulp_href', 'description', 'waiting', 'skipped', 'running', 'completed', 'canceled', 'failed')
class AccessPolicySerializer(ModelSerializer): """Serializer for AccessPolicy.""" pulp_href = IdentityField(view_name="access_policies-detail") permissions_assignment = serializers.ListField( child=serializers.DictField(), help_text= _("List of callables that define the new permissions to be created for new objects." ), ) statements = serializers.ListField( child=serializers.DictField(), help_text=_("List of policy statements defining the policy."), ) viewset_name = serializers.CharField( help_text=_("The name of ViewSet this AccessPolicy authorizes."), validators=[ UniqueValidator(queryset=models.AccessPolicy.objects.all()) ], ) class Meta: model = models.AccessPolicy fields = ModelSerializer.Meta.fields + ( "permissions_assignment", "statements", "viewset_name", )
class UserSerializer(serializers.ModelSerializer): """Serializer for User.""" pulp_href = IdentityField(view_name="users-detail") id = serializers.IntegerField(read_only=True) username = serializers.CharField( help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."), max_length=150, ) first_name = serializers.CharField(help_text=_("First name"), max_length=150, allow_blank=True) last_name = serializers.CharField(help_text=_("Last name"), max_length=150, allow_blank=True) email = serializers.EmailField(help_text=_("Email address"), allow_blank=True) is_staff = serializers.BooleanField( help_text=_("Designates whether the user can log into this admin site."), default=False ) is_active = serializers.BooleanField( help_text=_("Designates whether this user should be treated as active."), default=True ) date_joined = serializers.DateTimeField(help_text=_("Date joined"), read_only=True) groups = UserGroupSerializer(read_only=True, many=True) class Meta: model = get_user_model() fields = ( "pulp_href", "id", "username", "first_name", "last_name", "email", "is_staff", "is_active", "date_joined", "groups", )
class PublicationSerializer(ModelSerializer): _href = IdentityField( view_name='publications-detail' ) pass_through = serializers.BooleanField( help_text=_('The publication is a pass-through for the repository version.') ) publisher = DetailRelatedField( help_text=_('The publisher that created this publication.'), queryset=models.Publisher.objects.all() ) distributions = serializers.HyperlinkedRelatedField( help_text=_('This publication is currently being served as' 'defined by these distributions.'), many=True, read_only=True, view_name='distributions-detail', ) repository_version = NestedRelatedField( view_name='versions-detail', lookup_field='number', parent_lookup_kwargs={'repository_pk': 'repository__pk'}, read_only=True, ) class Meta: model = models.Publication fields = ModelSerializer.Meta.fields + ( 'pass_through', 'publisher', 'distributions', 'repository_version', )
class UserSerializer(ModelSerializer): _href = IdentityField(view_name='users-detail') username = serializers.CharField( help_text= _("Required. {} characters or fewer. Letters, digits and @/./+/-/_ only." ).format(User._meta.get_field('username').max_length), validators=[ UniqueValidator(queryset=User.objects.all()), validators.RegexValidator( regex=r'^[\w.@+-]+$', message=_( 'Enter a valid username. This value may contain only letters, numbers' ' and @/./+/-/_ characters.'), code='invalid'), validators.MaxLengthValidator( User._meta.get_field('username').max_length, message=_( 'The length of username must be less than {} characters'). format(User._meta.get_field('username').max_length)), ]) password = PasswordSerializer(help_text=_("Password"), write_only=True) class Meta: model = User fields = ModelSerializer.Meta.fields + ('username', 'password')
class TaskSerializer(ModelSerializer): _href = IdentityField(view_name='tasks-detail') state = serializers.CharField( help_text=_("The current state of the task. The possible values include:" " 'waiting', 'skipped', 'running', 'completed', 'failed' and 'canceled'."), read_only=True ) started_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task started execution."), read_only=True ) finished_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task stopped execution."), read_only=True ) non_fatal_errors = serializers.JSONField( help_text=_("A JSON Object of non-fatal errors encountered during the execution of this " "task."), read_only=True ) error = serializers.JSONField( help_text=_("A JSON Object of a fatal error encountered during the execution of this " "task."), read_only=True ) worker = RelatedField( help_text=_("The worker associated with this task." " This field is empty if a worker is not yet assigned."), read_only=True, view_name='workers-detail' ) parent = RelatedField( help_text=_("The parent task that spawned this task."), read_only=True, view_name='tasks-detail' ) spawned_tasks = RelatedField( help_text=_("Any tasks spawned by this task."), many=True, read_only=True, view_name='tasks-detail' ) progress_reports = ProgressReportSerializer( many=True, read_only=True ) created_resources = CreatedResourceSerializer( help_text=_('Resources created by this task.'), many=True, read_only=True, view_name='None' # This is a polymorphic field. The serializer does not need a view name. ) class Meta: model = models.Task fields = ModelSerializer.Meta.fields + ('state', 'started_at', 'finished_at', 'non_fatal_errors', 'error', 'worker', 'parent', 'spawned_tasks', 'progress_reports', 'created_resources')
class UserSerializer(serializers.ModelSerializer): """Serializer for User.""" pulp_href = IdentityField(view_name="users-detail") id = serializers.IntegerField(read_only=True) username = serializers.CharField( help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."), max_length=150, validators=[UniqueValidator(queryset=User.objects.all())], ) password = serializers.CharField( help_text=_("Users password. Set to ``null`` to disable password authentication."), write_only=True, allow_null=True, default=None, style={"input_type": "password"}, ) first_name = serializers.CharField( help_text=_("First name"), max_length=150, allow_blank=True, required=False ) last_name = serializers.CharField( help_text=_("Last name"), max_length=150, allow_blank=True, required=False ) email = serializers.EmailField(help_text=_("Email address"), allow_blank=True, required=False) is_staff = serializers.BooleanField( help_text=_("Designates whether the user can log into this admin site."), default=False ) is_active = serializers.BooleanField( help_text=_("Designates whether this user should be treated as active."), default=True ) date_joined = serializers.DateTimeField(help_text=_("Date joined"), read_only=True) groups = UserGroupSerializer(read_only=True, many=True) def validate(self, data): data = super().validate(data) if "password" in data: # `None` will automatically result in an unusable password if data["password"] is not None: validate_password(data["password"]) data["password"] = make_password(data["password"]) return data class Meta: model = User fields = ( "pulp_href", "id", "username", "password", "first_name", "last_name", "email", "is_staff", "is_active", "date_joined", "groups", )
class UserGroupSerializer(serializers.ModelSerializer): """Serializer for Groups that belong to an User.""" name = serializers.CharField(help_text=_("Name."), max_length=150) pulp_href = IdentityField(view_name="groups-detail") class Meta: model = Group fields = ("name", "pulp_href")
class GroupSerializer(ValidateFieldsMixin, serializers.ModelSerializer): """Serializer for Group.""" pulp_href = IdentityField(view_name="groups-detail") id = serializers.IntegerField(read_only=True) name = serializers.CharField(help_text=_("Name"), max_length=150) class Meta: model = Group fields = ("name", "pulp_href", "id")
class RepositorySerializer(ModelSerializer): _href = IdentityField(view_name='repositories-detail') _versions_href = IdentityField( view_name='versions-list', lookup_url_kwarg='repository_pk', ) _latest_version_href = LatestVersionField() name = serializers.CharField( help_text=_('A unique name for this repository.'), validators=[UniqueValidator(queryset=models.Repository.objects.all())]) description = serializers.CharField( help_text=_('An optional description.'), required=False, allow_null=True) class Meta: model = models.Repository fields = ModelSerializer.Meta.fields + ( '_versions_href', '_latest_version_href', 'name', 'description')
class GroupUserSerializer(ValidateFieldsMixin, serializers.ModelSerializer): """Serializer for Users that belong to a Group.""" username = serializers.CharField( help_text=_("Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."), max_length=150, ) pulp_href = IdentityField(view_name="users-detail") class Meta: model = get_user_model() fields = ("username", "pulp_href")
class AccessPolicySerializer(ModelSerializer): """Serializer for AccessPolicy.""" pulp_href = IdentityField(view_name="access_policies-detail") permissions_assignment = serializers.ListField( child=serializers.DictField(), help_text= _("List of callables that define the new permissions to be created for new objects." ), ) statements = serializers.ListField( child=serializers.DictField(), help_text=_("List of policy statements defining the policy."), ) viewset_name = serializers.CharField( help_text=_("The name of ViewSet this AccessPolicy authorizes."), validators=[ UniqueValidator(queryset=models.AccessPolicy.objects.all()) ], read_only=True, ) customized = serializers.BooleanField( help_text=_( "True if the AccessPolicy has been user-modified. False otherwise." ), read_only=True, ) class Meta: model = models.AccessPolicy fields = ModelSerializer.Meta.fields + ( "permissions_assignment", "statements", "viewset_name", "customized", ) def validate(self, data): """ " Validate the AccessPolicy. This ensures that the customized boolean will be set to True anytime the user modifies it. """ data = super().validate(data) if data: data["customized"] = True return data
class WorkerSerializer(ModelSerializer): pulp_href = IdentityField(view_name="workers-detail") name = serializers.CharField(help_text=_("The name of the worker."), read_only=True) last_heartbeat = serializers.DateTimeField( help_text=_("Timestamp of the last time the worker talked to the service."), read_only=True ) # disable "created" because we don't care about it created = None class Meta: model = models.Worker _base_fields = tuple(set(ModelSerializer.Meta.fields) - set(["created"])) fields = _base_fields + ("name", "last_heartbeat")
class TaskGroupSerializer(ModelSerializer): pulp_href = IdentityField(view_name="task-groups-detail") description = serializers.CharField( help_text=_("A description of the task group.")) all_tasks_dispatched = serializers.BooleanField(help_text=_( "Whether all tasks have been spawned for this task group.")) waiting = TaskGroupStatusCountField( state=TASK_STATES.WAITING, help_text=_("Number of tasks in the 'waiting' state")) skipped = TaskGroupStatusCountField( state=TASK_STATES.SKIPPED, help_text=_("Number of tasks in the 'skipped' state")) running = TaskGroupStatusCountField( state=TASK_STATES.RUNNING, help_text=_("Number of tasks in the 'running' state")) completed = TaskGroupStatusCountField( state=TASK_STATES.COMPLETED, help_text=_("Number of tasks in the 'completed' state")) canceled = TaskGroupStatusCountField( state=TASK_STATES.CANCELED, help_text=_("Number of tasks in the 'canceled' state")) failed = TaskGroupStatusCountField( state=TASK_STATES.FAILED, help_text=_("Number of tasks in the 'failed' state")) canceling = TaskGroupStatusCountField( state=TASK_STATES.CANCELING, help_text=_("Number of tasks in the 'canceling' state")) group_progress_reports = GroupProgressReportSerializer(many=True, read_only=True) tasks = MinimalTaskSerializer(many=True, read_only=True) class Meta: model = models.TaskGroup fields = ( "pulp_href", "description", "all_tasks_dispatched", "waiting", "skipped", "running", "completed", "canceled", "failed", "canceling", "group_progress_reports", "tasks", )
class RoleSerializer(ModelSerializer): """Serializer for Role.""" pulp_href = IdentityField(view_name="roles-detail") name = serializers.CharField( help_text=_("The name of this role."), validators=[UniqueValidator(queryset=Role.objects.all())], ) description = serializers.CharField( help_text=_("An optional description."), required=False, allow_null=True ) permissions = PermissionField( many=True, help_text=_("List of permissions defining the role."), ) locked = serializers.BooleanField( help_text=_("True if the role is system managed."), read_only=True, ) def create(self, validated_data): permissions = validated_data.pop("permissions") instance = super().create(validated_data) instance.permissions.set(permissions) return instance def update(self, instance, validated_data): permissions = validated_data.pop("permissions", None) instance = super().update(instance, validated_data) if permissions is not None: instance.permissions.set(permissions) return instance class Meta: model = Role fields = ModelSerializer.Meta.fields + ( "name", "description", "permissions", "locked", )
class WorkerSerializer(ModelSerializer): pulp_href = IdentityField(view_name="workers-detail") name = serializers.CharField(help_text=_("The name of the worker."), read_only=True) last_heartbeat = serializers.DateTimeField(help_text=_( "Timestamp of the last time the worker talked to the service."), read_only=True) current_task = RelatedField( help_text=_( "The task this worker is currently executing, or empty if the worker is not " "currently assigned to a task."), read_only=True, view_name="tasks-detail", ) class Meta: model = models.Worker fields = ModelSerializer.Meta.fields + ("name", "last_heartbeat", "current_task")
class TaskSerializer(ModelSerializer): pulp_href = IdentityField(view_name="tasks-detail") state = serializers.CharField( help_text=_( "The current state of the task. The possible values include:" " 'waiting', 'skipped', 'running', 'completed', 'failed', 'canceled' and 'canceling'." ), read_only=True, ) name = serializers.CharField(help_text=_("The name of task.")) logging_cid = serializers.CharField( help_text=_("The logging correlation id associated with this task") ) started_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task started execution."), read_only=True ) finished_at = serializers.DateTimeField( help_text=_("Timestamp of the when this task stopped execution."), read_only=True ) error = serializers.DictField( child=serializers.JSONField(), help_text=_( "A JSON Object of a fatal error encountered during the execution of this task." ), read_only=True, ) worker = RelatedField( help_text=_( "The worker associated with this task." " This field is empty if a worker is not yet assigned." ), read_only=True, view_name="workers-detail", ) parent_task = RelatedField( help_text=_("The parent task that spawned this task."), read_only=True, view_name="tasks-detail", ) child_tasks = RelatedField( help_text=_("Any tasks spawned by this task."), many=True, read_only=True, view_name="tasks-detail", ) task_group = RelatedField( help_text=_("The task group that this task is a member of."), read_only=True, view_name="task-groups-detail", ) progress_reports = ProgressReportSerializer(many=True, read_only=True) created_resources = CreatedResourceSerializer( help_text=_("Resources created by this task."), many=True, read_only=True, view_name="None", # This is a polymorphic field. The serializer does not need a view name. ) reserved_resources_record = serializers.ListField( child=serializers.CharField(), help_text=_("A list of resources required by that task."), read_only=True, ) class Meta: model = models.Task fields = ModelSerializer.Meta.fields + ( "state", "name", "logging_cid", "started_at", "finished_at", "error", "worker", "parent_task", "child_tasks", "task_group", "progress_reports", "created_resources", "reserved_resources_record", )
class BaseDistributionSerializer(ModelSerializer): _href = IdentityField(view_name='distributions-detail') name = serializers.CharField( help_text=_('A unique distribution name. Ex, `rawhide` and `stable`.'), validators=[ validators.MaxLengthValidator( models.Distribution._meta.get_field('name').max_length, message=_( 'Distribution name length must be less than {} characters' ).format( models.Distribution._meta.get_field('name').max_length)), UniqueValidator(queryset=models.Distribution.objects.all()) ]) base_path = serializers.CharField( help_text= _('The base (relative) path component of the published url. Avoid paths that \ overlap with other distribution base paths (e.g. "foo" and "foo/bar")' ), validators=[ validators.MaxLengthValidator( models.Distribution._meta.get_field('base_path').max_length, message= _('Distribution base_path length must be less than {} characters' ).format( models.Distribution._meta.get_field( 'base_path').max_length)), UniqueValidator(queryset=models.Distribution.objects.all()), ]) publisher = DetailRelatedField( required=False, help_text=_( 'Publications created by this publisher and repository are automatically' 'served as defined by this distribution'), queryset=models.Publisher.objects.all(), allow_null=True) content_guard = DetailRelatedField( required=False, help_text=_('An optional content-guard.'), queryset=models.ContentGuard.objects.all(), allow_null=True) publication = RelatedField( required=False, help_text=_( 'The publication being served as defined by this distribution'), queryset=models.Publication.objects.exclude(complete=False), view_name='publications-detail', allow_null=True) repository = serializers.HyperlinkedRelatedField( required=False, help_text=_( 'Publications created by this repository and publisher are automatically' 'served as defined by this distribution'), queryset=models.Repository.objects.all(), view_name='repositories-detail', allow_null=True) remote = DetailRelatedField( required=False, help_text= _('Remote that can be used to fetch content when using pull-through caching.' ), queryset=models.Remote.objects.all(), allow_null=True) class Meta: fields = ModelSerializer.Meta.fields + ( 'name', 'publisher', 'publication', 'repository', 'content_guard', 'remote', ) def validate(self, data): super().validate(data) if 'publisher' in data: publisher = data['publisher'] elif self.instance: publisher = self.instance.publisher else: publisher = None if 'repository' in data: repository = data['repository'] elif self.instance: repository = self.instance.repository else: repository = None if publisher and not repository: raise serializers.ValidationError({ 'repository': _("Repository must be set if " "publisher is set.") }) if repository and not publisher: raise serializers.ValidationError({ 'publisher': _("Publisher must be set if " "repository is set.") }) return data
class DistributionSerializer(ModelSerializer): _href = IdentityField( view_name='distributions-detail' ) name = serializers.CharField( help_text=_('A unique distribution name. Ex, `rawhide` and `stable`.'), validators=[validators.MaxLengthValidator( models.Distribution._meta.get_field('name').max_length, message=_('Distribution name length must be less than {} characters').format( models.Distribution._meta.get_field('name').max_length )), UniqueValidator(queryset=models.Distribution.objects.all())] ) base_path = serializers.CharField( help_text=_('The base (relative) path component of the published url. Avoid paths that \ overlap with other distribution base paths (e.g. "foo" and "foo/bar")'), validators=[validators.MaxLengthValidator( models.Distribution._meta.get_field('base_path').max_length, message=_('Distribution base_path length must be less than {} characters').format( models.Distribution._meta.get_field('base_path').max_length )), UniqueValidator(queryset=models.Distribution.objects.all()), ] ) publisher = DetailRelatedField( required=False, help_text=_('Publications created by this publisher and repository are automatically' 'served as defined by this distribution'), queryset=models.Publisher.objects.all(), allow_null=True ) content_guard = DetailRelatedField( required=False, help_text=_('An optional content-guard.'), queryset=models.ContentGuard.objects.all(), allow_null=True ) publication = RelatedField( required=False, help_text=_('The publication being served as defined by this distribution'), queryset=models.Publication.objects.exclude(complete=False), view_name='publications-detail', allow_null=True ) repository = serializers.HyperlinkedRelatedField( required=False, help_text=_('Publications created by this repository and publisher are automatically' 'served as defined by this distribution'), queryset=models.Repository.objects.all(), view_name='repositories-detail', allow_null=True ) base_url = BaseURLField( source='base_path', read_only=True, help_text=_('The URL for accessing the publication as defined by this distribution.') ) class Meta: model = models.Distribution fields = ModelSerializer.Meta.fields + ( 'name', 'base_path', 'publisher', 'publication', 'base_url', 'repository', 'content_guard', ) def _validate_path_overlap(self, path): # look for any base paths nested in path search = path.split("/")[0] q = Q(base_path=search) for subdir in path.split("/")[1:]: search = "/".join((search, subdir)) q |= Q(base_path=search) # look for any base paths that nest path q |= Q(base_path__startswith='{}/'.format(path)) qs = models.Distribution.objects.filter(q) if self.instance is not None: qs = qs.exclude(pk=self.instance.pk) match = qs.first() if match: raise serializers.ValidationError(detail=_("Overlaps with existing distribution '" "{}'").format(match.name)) return path def validate_base_path(self, path): self._validate_relative_path(path) return self._validate_path_overlap(path) def validate(self, data): super().validate(data) if 'publisher' in data: publisher = data['publisher'] elif self.instance: publisher = self.instance.publisher else: publisher = None if 'repository' in data: repository = data['repository'] elif self.instance: repository = self.instance.repository else: repository = None if publisher and not repository: raise serializers.ValidationError({'repository': _("Repository must be set if " "publisher is set.")}) if repository and not publisher: raise serializers.ValidationError({'publisher': _("Publisher must be set if " "repository is set.")}) return data
class AccessPolicySerializer(ModelSerializer): """Serializer for AccessPolicy.""" pulp_href = IdentityField(view_name="access_policies-detail") permissions_assignment = serializers.ListField( child=serializers.DictField(), help_text=_( "List of callables that define the new permissions to be created for new objects." "This is deprecated. Use `creation_hooks` instead." ), source="creation_hooks", required=False, ) creation_hooks = serializers.ListField( child=serializers.DictField(), help_text=_("List of callables that may associate user roles for new objects."), required=False, ) statements = serializers.ListField( child=serializers.DictField(), help_text=_("List of policy statements defining the policy."), ) viewset_name = serializers.CharField( help_text=_("The name of ViewSet this AccessPolicy authorizes."), validators=[UniqueValidator(queryset=models.AccessPolicy.objects.all())], read_only=True, ) customized = serializers.BooleanField( help_text=_("True if the AccessPolicy has been user-modified. False otherwise."), read_only=True, ) class Meta: model = models.AccessPolicy fields = ModelSerializer.Meta.fields + ( "permissions_assignment", "creation_hooks", "statements", "viewset_name", "customized", ) def validate(self, data): """ Validate the AccessPolicy. This ensures that the customized boolean will be set to True anytime the user modifies it. """ data = super().validate(data) if "permissions_assignment" in data: if "creation_hooks" in data: if data["creation_hooks"] != data["permissions_assignment"]: raise serializers.ValidationError( detail=_( "Cannot specify both 'permissions_assignment' and 'creation_hooks'." ) ) data["creation_hooks"] = data.pop("permissions_assignment") if data: data["customized"] = True return data