def run_validation(self, data=empty): """ We patch this function because we want to see all the errors at once. """ (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data errors = OrderedDict() try: data = self.to_internal_value(data) except ValidationError as exc: errors.update(exc.detail) try: self.run_validators(data) except (ValidationError, DjangoValidationError) as exc: errors.update(as_serializer_error(exc)) try: data = self.validate(data) assert data is not None, '.validate() should return the validated data' except (ValidationError, DjangoValidationError) as exc: errors.update(as_serializer_error(exc)) if errors: raise ValidationError(errors) return data
def run_validation(self, data=empty): (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data # mapping to internal values value, to_internal_errors = self.to_internal_value(data) # running validators validators_errors = OrderedDict() try: self.run_validators(value) except (ValidationError, DjangoValidationError) as exc: validators_errors = as_serializer_error(exc) # running final validation validation_errors = OrderedDict() try: value = self.validate(value) assert value is not None, ".validate() should return the validated data" except (ValidationError, DjangoValidationError) as exc: validation_errors = as_serializer_error(exc) # if there were any errors - raise the combination of them if to_internal_errors or validators_errors or validation_errors: # update dicts in reverse - to show most basic error for a given field if errors overlap validation_errors.update(validators_errors) validation_errors.update(to_internal_errors) raise ValidationError(detail=validation_errors) return value
def __exit__(self, exc_type, exc_val, exc_tb): if exc_type and issubclass(exc_type, ValidationError): error = exc_val if exc_val else ValidationError() if self.field_name not in self.serializer_instance._errors: self.serializer_instance._errors.update({self.field_name: [as_serializer_error(error)]}) else: self.serializer_instance._errors[self.field_name].append(as_serializer_error(error)) return True
def run_validation(self, data=empty): """ We override the default `run_validation`, because the validation performed by validators and the `.validate()` method should be coerced into an error dictionary with a 'non_fields_error' key. """ (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data result = self.to_internal_value(data) assert isinstance( result, dict), '.to_internal_value() should return the dict data type' value = result.get('ret') errors = result.get('errors') try: self.run_validators(value) value = self.validate(value) assert value is not None, '.validate() should return the validated data' except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=as_serializer_error(exc)) return {'value': value, 'errors': errors}
def validate_user_password(password): try: validate_password(password) except exceptions.ValidationError as e: serializer_error = serializers.as_serializer_error(e) raise serializers.ValidationError(serializer_error["non_field_errors"]) return password
def full_clean(self): """ Performs full validation, additionally performing validation using the validators from InteractionSerializer if the interaction was matched to a contact. Errors are mapped to CSV fields where possible. If not possible, they are added to NON_FIELD_ERRORS (but this should not happen). """ super().full_clean() if not self.is_valid_and_matched(): return transformed_data = self.cleaned_data_as_serializer_dict() serializer = InteractionSerializer( context={'check_association_permissions': False}) try: serializer.run_validators(transformed_data) except serializers.ValidationError as exc: # Make sure that errors are wrapped in a dict, and values are always a list normalised_errors = serializers.as_serializer_error(exc) for field, errors in normalised_errors.items(): self._add_serializer_error(field, errors)
def save(self): """POST: Perform final save of submitted BOM data: - By this stage each line in the BOM has been validated - Individually 'save' (create) each BomItem line """ data = self.validated_data items = data['items'] try: with transaction.atomic(): for item in items: part = item['part'] sub_part = item['sub_part'] # Ignore duplicate BOM items if BomItem.objects.filter(part=part, sub_part=sub_part).exists(): continue # Create a new BomItem object BomItem.objects.create(**item) except Exception as e: raise serializers.ValidationError( detail=serializers.as_serializer_error(e))
def save(self): data = self.validated_data items = data.get('items', []) build = self.context['build'] with transaction.atomic(): for item in items: bom_item = item['bom_item'] stock_item = item['stock_item'] quantity = item['quantity'] output = item.get('output', None) try: # Create a new BuildItem to allocate stock BuildItem.objects.create(build=build, bom_item=bom_item, stock_item=stock_item, quantity=quantity, install_into=output) except (ValidationError, DjangoValidationError) as exc: # Catch model errors and re-throw as DRF errors raise ValidationError( detail=serializers.as_serializer_error(exc))
def run_validation(self, data=empty): """ Perform serializer validation. In addition to running validators on the serializer fields, this class ensures that the underlying model is also validated. """ # Run any native validation checks first (may raise a ValidationError) data = super().run_validation(data) # Now ensure the underlying model is correct if not hasattr(self, 'instance') or self.instance is None: # No instance exists (we are creating a new one) instance = self.Meta.model(**data) else: # Instance already exists (we are updating!) instance = self.instance # Update instance fields for attr, value in data.items(): setattr(instance, attr, value) # Run a 'full_clean' on the model. # Note that by default, DRF does *not* perform full model validation! try: instance.full_clean() except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=serializers.as_serializer_error(exc)) return data
def validate(self, attrs): user = self.context["request"].user old_password = attrs['old_password'] new_password = attrs['new_password'] if old_password == new_password: raise serializers.ValidationError({ 'new_password': '******' }) if user.check_password(old_password): try: validate_password(new_password, user) except django_exceptions.ValidationError as e: serializer_errors = serializers.as_serializer_error(e) raise serializers.ValidationError({ 'new_password': serializer_errors[ api_settings.NON_FIELD_ERRORS_KEY] }) else: raise serializers.ValidationError({ 'old_password': '******' }) return attrs
def create(self, data): logger.debug('QuestionAnswerSerializer.create(%r)', data) question = data['question'] if isinstance(question, models.TextQuestion): answer = models.TextAnswer(**data) if isinstance(question, models.ChoiceQuestion): choices = data.pop('choices', []) answer = models.ChoiceAnswer(**data) try: answer.full_clean() answer.save(force_insert=True) except _djexc.ValidationError as exc: # @xxx: {'__all__': [...]} -> {'non_field_errors': [...]} raise _drfexcs.ValidationError(detail=_drfsers.as_serializer_error(exc)) if isinstance(question, models.ChoiceQuestion) and choices: try: # @todo: transaction answer.choices.set(choices) answer.save() except Exception: answer.delete() logger.exception('Unhandled error') self.fail('internal_error') except: answer.delete() raise return answer
def filter_queryset(self, request, queryset, view): try: if self.filter_param_name: filter_value_raw = request.GET.get(self.filter_param_name, "") if filter_value_raw != "": filter = self.build_filter(filter_value_raw) queryset = queryset.filter(filter) except ParseException as e: raise BadQuery("Filtering position: {0}".format(e.col)) except django_exceptions.ValidationError as exc: raise rest_exceptions.ValidationError( detail=as_serializer_error(exc)) except django_exceptions.FieldError as exc: raise BadQuery("Bad filter: {0}".format(exc)) try: if self.sort_param_name: sort_value_raw = request.GET.get(self.sort_param_name, "") if sort_value_raw != "": sort_value = self.build_sort(sort_value_raw) if sort_value: queryset = queryset.order_by(*sort_value) except ParseException as e: raise BadQuery("Sorting error position: {0}".format(e.col)) except django_exceptions.FieldError as exc: raise BadQuery("Bad sorting: {0}".format(exc)) #print queryset.query return queryset
def save(self): """ Perform the actual database transaction to receive purchase order items """ data = self.validated_data request = self.context['request'] order = self.context['order'] items = data['items'] location = data.get('location', None) # Now we can actually receive the items into stock with transaction.atomic(): for item in items: # Select location loc = item.get('location', None) or item['line_item'].get_destination() or location try: order.receive_line_item( item['line_item'], loc, item['quantity'], request.user, status=item['status'], barcode=item.get('barcode', ''), ) except (ValidationError, DjangoValidationError) as exc: # Catch model errors and re-throw as DRF errors raise ValidationError(detail=serializers.as_serializer_error(exc))
def save(self): data = self.validated_data items = data['items'] try: with transaction.atomic(): for item in items: part = item['part'] sub_part = item['sub_part'] # Ignore duplicate BOM items if BomItem.objects.filter(part=part, sub_part=sub_part).exists(): continue # Create a new BomItem object BomItem.objects.create(**item) except Exception as e: raise serializers.ValidationError( detail=serializers.as_serializer_error(e))
def validate(self, attrs): if attrs.get("plugin_data"): section = attrs["section"] try: if not section.plugin_identifier: raise ValidationError( "The section %s has no plugin; no plugin data is allowed." % section) plugin = section.plugin_implementation attrs["plugin_data"] = plugin.clean_client_data( attrs["plugin_data"]) except (ValidationError, DjangoValidationError) as ve: # Massage the validation error slightly... detail = as_serializer_error(ve) detail.setdefault("plugin_data", []).extend( detail.pop(api_settings.NON_FIELD_ERRORS_KEY, ())) raise ValidationError(detail=detail) attrs["plugin_identifier"] = section.plugin_identifier if not any([ attrs.get(field) for field in SectionComment.fields_to_check_for_data ]): raise ValidationError( "You must supply at least one of the following data in a comment: " + str(SectionComment.fields_to_check_for_data)) return attrs
def has_permission(self, request: Request, view) -> bool: # permission checks run before the handler is determined. if there is no handler, # a "method is not allowed" must be raised, not an HTTP 403 (see #385) # this implementation works for both APIView and viewsets has_handler = hasattr(view, request.method.lower()) if not has_handler: view.http_method_not_allowed(request) # JWTs are only valid for a short amount of time self.check_jwt_expiry(request.jwt_auth.payload) from rest_framework.viewsets import ViewSetMixin if bypass_permissions(request): return True scopes_required = get_required_scopes(view) component = self.get_component(view) if not self.permission_fields: return request.jwt_auth.has_auth(scopes_required, component) main_resource = self.get_main_resource() if view.action == "create": if view.__class__ is main_resource: main_object_data = request.data else: main_object_url = request.data[view.permission_main_object] main_object_path = urlparse(main_object_url).path try: main_object = get_resource_for_path(main_object_path) except ObjectDoesNotExist: raise ValidationError({ view.permission_main_object: ValidationError( _("The object does not exist in the database"), code="object-does-not-exist", ).detail }) except DjangoValidationError as exc: err_dict = as_serializer_error( ValidationError({view.permission_main_object: exc})) raise ValidationError(err_dict) main_object_data = self.format_data(main_object, request) fields = self.get_fields(main_object_data) return request.jwt_auth.has_auth(scopes_required, component, **fields) # detect if this is an unsupported method - if it's a viewset and the # action was not mapped, it's not supported and DRF will catch it if view.action is None and isinstance(view, ViewSetMixin): return True # by default - check if the action is allowed at all return request.jwt_auth.has_auth(scopes_required, component)
def get(self, request, *args, **kwargs): try: self.queryset.filter(user=request.user, read=False).update(read=True) return Response({'status': 'ok'}) except Exception as exc: raise serializers.ValidationError( detail=serializers.as_serializer_error(exc))
def perform_create(self, serializer): message = self.get_object() try: message.read = self.target message.save() except Exception as exc: raise serializers.ValidationError( detail=serializers.as_serializer_error(exc))
def update(self, instance, validated_data): """Catch any django ValidationError, and re-throw as a DRF ValidationError.""" try: instance = super().update(instance, validated_data) except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=serializers.as_serializer_error(exc)) return instance
def save(self, **kwargs): """Catch any django ValidationError thrown at the moment `save` is called, and re-throw as a DRF ValidationError.""" try: super().save(**kwargs) except (ValidationError, DjangoValidationError) as exc: raise ValidationError(detail=serializers.as_serializer_error(exc)) return self.instance
def perform_create(self, serializer): """Set the `read` status to the target value.""" message = self.get_object() try: message.read = self.target message.save() except Exception as exc: raise serializers.ValidationError( detail=serializers.as_serializer_error(exc))
def to_representation(self, status): data = super().to_representation(status) order = self.context['order'] try: app_settings.SALESMAN_ORDER_STATUS.validate_transition(status, order) except (ValidationError, DjangoValidationError) as e: error = serializers.as_serializer_error(e) data['error'] = error[api_settings.NON_FIELD_ERRORS_KEY][0] return data
def validate(self, attrs): user = User(**attrs) password = attrs.get("password") try: validate_password(password, user) except django_exceptions.ValidationError as e: serializer_error = serializers.as_serializer_error(e) raise serializers.ValidationError( {"password": serializer_error["non_field_errors"]}) return attrs
def exception_handler(exc, context): """ Custom exception handler for DRF framework. Ref: https://www.django-rest-framework.org/api-guide/exceptions/#custom-exception-handling Catches any errors not natively handled by DRF, and re-throws as an error DRF can handle """ response = None # Catch any django validation error, and re-throw a DRF validation error if isinstance(exc, DjangoValidationError): exc = DRFValidationError(detail=serializers.as_serializer_error(exc)) # Default to the built-in DRF exception handler response = drfviews.exception_handler(exc, context) if response is None: # DRF handler did not provide a default response for this exception if settings.DEBUG: error_detail = str(exc) else: error_detail = _("Error details can be found in the admin panel") response_data = { 'error': type(exc).__name__, 'error_class': str(type(exc)), 'detail': error_detail, 'path': context['request'].path, 'status_code': 500, } response = Response(response_data, status=500) # Log the exception to the database, too kind, info, data = sys.exc_info() Error.objects.create( kind=kind.__name__, info=info, data='\n'.join(traceback.format_exception(kind, info, data)), path=context['request'].path, html=ExceptionReporter(context['request'], kind, info, data).get_traceback_html(), ) if response is not None: # Convert errors returned under the label '__all__' to 'non_field_errors' if '__all__' in response.data: response.data['non_field_errors'] = response.data['__all__'] del response.data['__all__'] return response
def update(self, instance, validated_data): if isinstance(instance, models.Manager): instance = instance.all() exists_instances = {i.pk: i for i in instance} excess_instances_pks = set(exists_instances.keys()) model = self.child.Meta.model result = list() errors = list() has_error = False for data in validated_data: errors.append(dict()) # PK used to detect exists objects. try: pk = get_attribute(data, self.child.pk_field.source_attrs) except KeyError: pk = None try: if pk: if pk not in exists_instances: raise serializers.ValidationError({ self.child.pk_field.field_name: _('{} with pk `{}` doesn\'t exists.').format( model._meta.verbose_name.title(), pk), }) if pk not in excess_instances_pks: raise serializers.ValidationError({ self.child.pk_field.field_name: _('Duplication {} with pk `{}`.').format( model._meta.verbose_name, pk) }) excess_instances_pks.remove(pk) result.append(self.child.update(exists_instances[pk], data)) else: result.append(self.child.create(data)) except serializers.ValidationError as exc: has_error = True errors[-1] = serializers.as_serializer_error(exc) if has_error: raise serializers.ValidationError(errors) if excess_instances_pks and not getattr(self.root, 'partial', False): model._default_manager.filter(pk__in=excess_instances_pks).delete() return result
def test_nested_validation_error_detail(self): """ Ensure nested validation error detail is rendered correctly. """ e = serializers.ValidationError({'nested': { 'field': ['error'], }}) self.assertEqual(serializers.as_serializer_error(e), {'nested': { 'field': ['error'], }})
def validate(self, attrs): user = User(**attrs) password = attrs.get('password') try: validate_password(password, user) except ValidationError as e: serializer_error = serializers.as_serializer_error(e) raise serializers.ValidationError( {'password': serializer_error['non_field_errors']}) return attrs
def validate(self, attrs): user = User(**attrs) password = attrs.get('password') try: validate_password(password, user, self.password_validators()) except django_exceptions.ValidationError as e: serializer_error = serializers.as_serializer_error(e) raise serializers.ValidationError({ 'password': serializer_error[api_settings.NON_FIELD_ERRORS_KEY] }) return attrs
def run_validation(self, data=empty): def merge_err_dict(errs, new_errors): errs_copy = {**errs, **new_errors} for key, value in errs_copy.items(): if key in errs and key in new_errors: errs_copy[key] = value + errs[key] return errs_copy (is_empty_value, data) = self.validate_empty_values(data) if is_empty_value: return data errs = {} value = None try: value = self.to_internal_value(data) except (ValidationError, DjangoValidationError) as exc: errs = as_serializer_error(exc) try: self.run_validators(data) except (ValidationError, DjangoValidationError) as exc: errs = merge_err_dict(errs, as_serializer_error(exc)) try: if value: value = self.validate(value) else: value = self.validate(data) assert value is not None, \ '.validate() should return the validated data' except (ValidationError, DjangoValidationError) as exc: errs = merge_err_dict(errs, as_serializer_error(exc)) if errs: raise serializers.ValidationError(errs) else: return value
def validate(self, attrs): user = User(**attrs) password = attrs.get('password') try: validate_password(password, user) except django_exceptions.ValidationError as e: serializer_error = serializers.as_serializer_error(e) raise serializers.ValidationError({ 'password': serializer_error['non_field_errors'] }) return attrs
def to_representation(self, payment_method): data = super().to_representation(payment_method) payment = payment_methods_pool.get_payment(payment_method.identifier) request = self.context['request'] try: if 'basket' in self.context: payment.validate_basket(basket=self.context['basket'], request=request) if 'order' in self.context: payment.validate_order(order=self.context['order'], request=request) except (ValidationError, DjangoValidationError) as e: error = serializers.as_serializer_error(e) data['error'] = error[api_settings.NON_FIELD_ERRORS_KEY][0] return data
def test_nested_validation_error_detail(self): """ Ensure nested validation error detail is rendered correctly. """ e = serializers.ValidationError({ 'nested': { 'field': ['error'], } }) assert serializers.as_serializer_error(e) == { 'nested': { 'field': ['error'], } }