mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-15 18:52:27 +03:00
Merge 888df27dc3
into 2ae8c117da
This commit is contained in:
commit
89f93ab570
|
@ -170,6 +170,74 @@ If you want the date field to be entirely hidden from the user, then use `Hidden
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## MaxFileSizeValidator and MinFileSizeValidator
|
||||||
|
|
||||||
|
These validators can be used to enforce file size constraints on uploaded files. They are especially useful for `FileField` and `ImageField` in serializers.
|
||||||
|
|
||||||
|
### MaxFileSizeValidator
|
||||||
|
|
||||||
|
Ensures that the uploaded file does not exceed a maximum size (in bytes).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
* `max_size` (*required*) — The maximum file size in bytes.
|
||||||
|
* `message` — Custom error message. May use `{max_size}` in the string.
|
||||||
|
* `code` — Custom error code. Default is `'max_file_size'`.
|
||||||
|
|
||||||
|
### MinFileSizeValidator
|
||||||
|
|
||||||
|
Ensures that the uploaded file meets a minimum size (in bytes).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
* `min_size` (*required*) — The minimum file size in bytes.
|
||||||
|
* `message` — Custom error message. May use `{min_size}` in the string.
|
||||||
|
* `code` — Custom error code. Default is `'min_file_size'`.
|
||||||
|
|
||||||
|
### Usage Examples
|
||||||
|
|
||||||
|
#### Basic usage with FileField
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.validators import MaxFileSizeValidator, MinFileSizeValidator
|
||||||
|
|
||||||
|
class FileUploadSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MaxFileSizeValidator(1024 * 1024), # 1MB max
|
||||||
|
MinFileSizeValidator(1024), # 1KB min
|
||||||
|
])
|
||||||
|
|
||||||
|
#### Usage with ImageField
|
||||||
|
|
||||||
|
class ImageUploadSerializer(serializers.Serializer):
|
||||||
|
image = serializers.ImageField(validators=[
|
||||||
|
MaxFileSizeValidator(5 * 1024 * 1024), # 5MB max
|
||||||
|
MinFileSizeValidator(1024), # 1KB min
|
||||||
|
])
|
||||||
|
|
||||||
|
#### Custom error messages and codes
|
||||||
|
|
||||||
|
class CustomFileSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MaxFileSizeValidator(
|
||||||
|
max_size=1024 * 1024,
|
||||||
|
message="File size cannot exceed {max_size} bytes",
|
||||||
|
code='file_too_large'
|
||||||
|
),
|
||||||
|
MinFileSizeValidator(
|
||||||
|
min_size=1024,
|
||||||
|
message="File must be at least {min_size} bytes",
|
||||||
|
code='file_too_small'
|
||||||
|
),
|
||||||
|
])
|
||||||
|
|
||||||
|
### Error Codes
|
||||||
|
|
||||||
|
* `max_file_size` — Default error code for MaxFileSizeValidator
|
||||||
|
* `min_file_size` — Default error code for MinFileSizeValidator
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Advanced field defaults
|
# Advanced field defaults
|
||||||
|
|
||||||
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
|
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
|
||||||
|
|
|
@ -9,6 +9,7 @@ object creation, and makes it possible to switch between using the implicit
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.db import DataError
|
from django.db import DataError
|
||||||
from django.db.models import Exists
|
from django.db.models import Exists
|
||||||
|
from django.utils.deconstruct import deconstructible
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
@ -344,3 +345,73 @@ class UniqueForYearValidator(BaseUniqueForValidator):
|
||||||
filter_kwargs[field_name] = value
|
filter_kwargs[field_name] = value
|
||||||
filter_kwargs['%s__year' % date_field_name] = date.year
|
filter_kwargs['%s__year' % date_field_name] = date.year
|
||||||
return qs_filter(queryset, **filter_kwargs)
|
return qs_filter(queryset, **filter_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@deconstructible
|
||||||
|
class MaxFileSizeValidator:
|
||||||
|
"""
|
||||||
|
Validator that ensures uploaded files do not exceed a maximum size.
|
||||||
|
|
||||||
|
Should be applied to individual file fields on the serializer.
|
||||||
|
"""
|
||||||
|
message = _('File size must not exceed {max_size} bytes.')
|
||||||
|
code = 'max_file_size'
|
||||||
|
|
||||||
|
def __init__(self, max_size, message=None, code=None):
|
||||||
|
self.max_size = max_size
|
||||||
|
self.message = message or self.message
|
||||||
|
self.code = code or self.code
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
if hasattr(value, 'size'):
|
||||||
|
if value.size > self.max_size:
|
||||||
|
message = self.message.format(max_size=self.max_size)
|
||||||
|
raise ValidationError(message, code=self.code)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return NotImplemented
|
||||||
|
return (self.message == other.message
|
||||||
|
and self.code == other.code
|
||||||
|
and self.max_size == other.max_size)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s(max_size=%s)>' % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
smart_repr(self.max_size)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@deconstructible
|
||||||
|
class MinFileSizeValidator:
|
||||||
|
"""
|
||||||
|
Validator that ensures uploaded files meet a minimum size.
|
||||||
|
|
||||||
|
Should be applied to individual file fields on the serializer.
|
||||||
|
"""
|
||||||
|
message = _('File size must be at least {min_size} bytes.')
|
||||||
|
code = 'min_file_size'
|
||||||
|
|
||||||
|
def __init__(self, min_size, message=None, code=None):
|
||||||
|
self.min_size = min_size
|
||||||
|
self.message = message or self.message
|
||||||
|
self.code = code or self.code
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
if hasattr(value, 'size'):
|
||||||
|
if value.size < self.min_size:
|
||||||
|
message = self.message.format(min_size=self.min_size)
|
||||||
|
raise ValidationError(message, code=self.code)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, self.__class__):
|
||||||
|
return NotImplemented
|
||||||
|
return (self.message == other.message
|
||||||
|
and self.code == other.code
|
||||||
|
and self.min_size == other.min_size)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s(min_size=%s)>' % (
|
||||||
|
self.__class__.__name__,
|
||||||
|
smart_repr(self.min_size)
|
||||||
|
)
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import io
|
||||||
import re
|
import re
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django import VERSION as django_version
|
from django import VERSION as django_version
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.db import DataError, models
|
from django.db import DataError, models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.validators import (
|
from rest_framework.validators import (
|
||||||
BaseUniqueForValidator, UniqueTogetherValidator, UniqueValidator, qs_exists
|
BaseUniqueForValidator, MaxFileSizeValidator, MinFileSizeValidator,
|
||||||
|
UniqueTogetherValidator, UniqueValidator, qs_exists
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -972,3 +975,291 @@ class ValidatorsTests(TestCase):
|
||||||
assert validator == validator2
|
assert validator == validator2
|
||||||
validator2.date_field = "bar2"
|
validator2.date_field = "bar2"
|
||||||
assert validator != validator2
|
assert validator != validator2
|
||||||
|
|
||||||
|
|
||||||
|
# Tests for `MaxFileSizeValidator` and `MinFileSizeValidator`
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
class TestFileSizeValidators(TestCase):
|
||||||
|
class DummyFile:
|
||||||
|
def __init__(self, size):
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
def test_max_file_size_validator_pass(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
file = self.DummyFile(1024)
|
||||||
|
# Should not raise
|
||||||
|
validator(file)
|
||||||
|
|
||||||
|
def test_max_file_size_validator_fail(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
file = self.DummyFile(1025)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert 'File size must not exceed 1024 bytes.' in str(exc.value)
|
||||||
|
assert exc.value.get_codes() == ['max_file_size']
|
||||||
|
|
||||||
|
def test_min_file_size_validator_pass(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
file = self.DummyFile(200)
|
||||||
|
# Should not raise
|
||||||
|
validator(file)
|
||||||
|
|
||||||
|
def test_min_file_size_validator_fail(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
file = self.DummyFile(50)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert 'File size must be at least 100 bytes.' in str(exc.value)
|
||||||
|
assert exc.value.get_codes() == ['min_file_size']
|
||||||
|
|
||||||
|
def test_max_file_size_validator_exact_size(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
file = self.DummyFile(1024)
|
||||||
|
# Should not raise for exact size
|
||||||
|
validator(file)
|
||||||
|
|
||||||
|
def test_min_file_size_validator_exact_size(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
file = self.DummyFile(100)
|
||||||
|
# Should not raise for exact size
|
||||||
|
validator(file)
|
||||||
|
|
||||||
|
def test_max_file_size_validator_zero_size(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
file = self.DummyFile(0)
|
||||||
|
# Should not raise for zero size
|
||||||
|
validator(file)
|
||||||
|
|
||||||
|
def test_min_file_size_validator_zero_size(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
file = self.DummyFile(0)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert 'File size must be at least 100 bytes.' in str(exc.value)
|
||||||
|
|
||||||
|
def test_max_file_size_validator_no_size_attribute(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
# Object without size attribute should not raise
|
||||||
|
validator("not a file")
|
||||||
|
|
||||||
|
def test_min_file_size_validator_no_size_attribute(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
# Object without size attribute should not raise
|
||||||
|
validator("not a file")
|
||||||
|
|
||||||
|
def test_max_file_size_validator_custom_message(self):
|
||||||
|
validator = MaxFileSizeValidator(1024, message="File too big: {max_size}")
|
||||||
|
file = self.DummyFile(1025)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert 'File too big: 1024' in str(exc.value)
|
||||||
|
|
||||||
|
def test_min_file_size_validator_custom_message(self):
|
||||||
|
validator = MinFileSizeValidator(100, message="File too small: {min_size}")
|
||||||
|
file = self.DummyFile(50)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert 'File too small: 100' in str(exc.value)
|
||||||
|
|
||||||
|
def test_max_file_size_validator_custom_code(self):
|
||||||
|
validator = MaxFileSizeValidator(1024, code='custom_max_size')
|
||||||
|
file = self.DummyFile(1025)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert exc.value.get_codes() == ['custom_max_size']
|
||||||
|
|
||||||
|
def test_min_file_size_validator_custom_code(self):
|
||||||
|
validator = MinFileSizeValidator(100, code='custom_min_size')
|
||||||
|
file = self.DummyFile(50)
|
||||||
|
with pytest.raises(ValidationError) as exc:
|
||||||
|
validator(file)
|
||||||
|
assert exc.value.get_codes() == ['custom_min_size']
|
||||||
|
|
||||||
|
def test_max_file_size_validator_equality(self):
|
||||||
|
validator1 = MaxFileSizeValidator(1024)
|
||||||
|
validator2 = MaxFileSizeValidator(1024)
|
||||||
|
validator3 = MaxFileSizeValidator(2048)
|
||||||
|
validator4 = MaxFileSizeValidator(1024, message="Custom")
|
||||||
|
|
||||||
|
assert validator1 == validator2
|
||||||
|
assert validator1 != validator3
|
||||||
|
assert validator1 != validator4
|
||||||
|
|
||||||
|
def test_min_file_size_validator_equality(self):
|
||||||
|
validator1 = MinFileSizeValidator(100)
|
||||||
|
validator2 = MinFileSizeValidator(100)
|
||||||
|
validator3 = MinFileSizeValidator(200)
|
||||||
|
validator4 = MinFileSizeValidator(100, message="Custom")
|
||||||
|
|
||||||
|
assert validator1 == validator2
|
||||||
|
assert validator1 != validator3
|
||||||
|
assert validator1 != validator4
|
||||||
|
|
||||||
|
def test_max_file_size_validator_repr(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
assert repr(validator) == '<MaxFileSizeValidator(max_size=1024)>'
|
||||||
|
|
||||||
|
def test_min_file_size_validator_repr(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
assert repr(validator) == '<MinFileSizeValidator(min_size=100)>'
|
||||||
|
|
||||||
|
def test_max_file_size_validator_boundary_conditions(self):
|
||||||
|
validator = MaxFileSizeValidator(1024)
|
||||||
|
# One byte over
|
||||||
|
file = self.DummyFile(1025)
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
validator(file)
|
||||||
|
# One byte under
|
||||||
|
file = self.DummyFile(1023)
|
||||||
|
validator(file) # Should not raise
|
||||||
|
|
||||||
|
def test_min_file_size_validator_boundary_conditions(self):
|
||||||
|
validator = MinFileSizeValidator(100)
|
||||||
|
# One byte under
|
||||||
|
file = self.DummyFile(99)
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
validator(file)
|
||||||
|
# One byte over
|
||||||
|
file = self.DummyFile(101)
|
||||||
|
validator(file) # Should not raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileSizeValidatorIntegration(TestCase):
|
||||||
|
def test_filefield_max_and_min_size(self):
|
||||||
|
class FileSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MaxFileSizeValidator(10),
|
||||||
|
MinFileSizeValidator(5),
|
||||||
|
])
|
||||||
|
|
||||||
|
# File of size 7 (should pass)
|
||||||
|
file = SimpleUploadedFile('test.txt', b'1234567')
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# File of size 4 (too small)
|
||||||
|
file = SimpleUploadedFile('test.txt', b'1234')
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'min_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
||||||
|
# File of size 12 (too large)
|
||||||
|
file = SimpleUploadedFile('test.txt', b'123456789012')
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'max_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
||||||
|
def test_filefield_max_size_only(self):
|
||||||
|
class FileSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MaxFileSizeValidator(1024),
|
||||||
|
])
|
||||||
|
|
||||||
|
# File under limit (should pass)
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 512)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# File over limit
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 2048)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'max_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
||||||
|
def test_filefield_min_size_only(self):
|
||||||
|
class FileSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MinFileSizeValidator(100),
|
||||||
|
])
|
||||||
|
|
||||||
|
# File over minimum (should pass)
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 200)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# File under minimum
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 50)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'min_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
||||||
|
def test_imagefield_max_size(self):
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
except ImportError:
|
||||||
|
pytest.skip("PIL not available")
|
||||||
|
|
||||||
|
class ImageSerializer(serializers.Serializer):
|
||||||
|
image = serializers.ImageField(validators=[MaxFileSizeValidator(1024)])
|
||||||
|
|
||||||
|
# Create a small image in memory
|
||||||
|
img = Image.new('RGB', (10, 10), color='red')
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.save(buf, format='PNG')
|
||||||
|
buf.seek(0)
|
||||||
|
image_bytes = buf.read()
|
||||||
|
image_file = SimpleUploadedFile('test.png', image_bytes, content_type='image/png')
|
||||||
|
serializer = ImageSerializer(data={'image': image_file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# Create a valid image, then pad it to exceed the size limit
|
||||||
|
big_bytes = image_bytes + b'0' * (1025 - len(image_bytes))
|
||||||
|
big_image_file = SimpleUploadedFile('big.png', big_bytes, content_type='image/png')
|
||||||
|
serializer = ImageSerializer(data={'image': big_image_file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'max_file_size' in serializer.errors['image'][0].code
|
||||||
|
|
||||||
|
def test_imagefield_min_size(self):
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
except ImportError:
|
||||||
|
pytest.skip("PIL not available")
|
||||||
|
|
||||||
|
class ImageSerializer(serializers.Serializer):
|
||||||
|
image = serializers.ImageField(validators=[MinFileSizeValidator(100)])
|
||||||
|
|
||||||
|
# Create a large image in memory
|
||||||
|
img = Image.new('RGB', (50, 50), color='blue')
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.save(buf, format='PNG')
|
||||||
|
buf.seek(0)
|
||||||
|
image_bytes = buf.read()
|
||||||
|
image_file = SimpleUploadedFile('test.png', image_bytes, content_type='image/png')
|
||||||
|
serializer = ImageSerializer(data={'image': image_file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# Create a small image
|
||||||
|
small_img = Image.new('RGB', (5, 5), color='green')
|
||||||
|
small_buf = io.BytesIO()
|
||||||
|
small_img.save(small_buf, format='PNG')
|
||||||
|
small_buf.seek(0)
|
||||||
|
small_image_bytes = small_buf.read()
|
||||||
|
small_image_file = SimpleUploadedFile('small.png', small_image_bytes, content_type='image/png')
|
||||||
|
serializer = ImageSerializer(data={'image': small_image_file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'min_file_size' in serializer.errors['image'][0].code
|
||||||
|
|
||||||
|
def test_multiple_validators_on_same_field(self):
|
||||||
|
class FileSerializer(serializers.Serializer):
|
||||||
|
file = serializers.FileField(validators=[
|
||||||
|
MaxFileSizeValidator(1000),
|
||||||
|
MinFileSizeValidator(100),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Valid file
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 500)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert serializer.is_valid(), serializer.errors
|
||||||
|
|
||||||
|
# Too small
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 50)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'min_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
||||||
|
# Too large
|
||||||
|
file = SimpleUploadedFile('test.txt', b'x' * 1500)
|
||||||
|
serializer = FileSerializer(data={'file': file})
|
||||||
|
assert not serializer.is_valid()
|
||||||
|
assert 'max_file_size' in serializer.errors['file'][0].code
|
||||||
|
|
Loading…
Reference in New Issue
Block a user