I have a model Message
that has a FileField
. My API accepts files in Base64
encoding so they can be sent alongside other data.
To know a filename and an extension, there is one more field attachment_filename
in the serializer that is not a model field. It is used inside Base64Field
.
I want to be able to validate if there are both attachment_filename
, attachment
, or none of them.
The problem is that if the attachment_filename
is read-only, it is not present in validate
– data
variable.
On the other hand, if it’s required=False
, allow_null=True
, the serializer raises an error when creating a message:
TypeError: ChatMessage() got an unexpected keyword argument 'attachment_filename'
Code:
class Base64File(Base64FileField): # todo make accept a list of extensions (finite eg. pdf, xlsx, csv, txt ) ALLOWED_TYPES = ['pdf', 'xlsx', 'png', 'jpg', 'jpeg', 'docx', 'doc', 'zip'] def get_file_extension(self, filename, decoded_file): extension = self.get_full_name().split('.')[-1] return extension def get_file_name(self, decoded_file): attachment_filename = self.get_full_name() return '.'.join(attachment_filename.split('.')[:-1]) def get_full_name(self): return self.context['request'].data['attachment_filename'] # todo validate name class ChatMessageSerializer(serializers.ModelSerializer): attachment = Base64File(required=False) attachment_filename = serializers.CharField(required=False, allow_null=True) class Meta: model = ChatMessage fields = '__all__' def validate(self, data): """ Validation of start and end date. """ attachment = data.get('attachment') attachment_filename = data.get('attachment_filename') if bool(attachment) ^ bool(attachment_filename): raise serializers.ValidationError("Either none or both 'attachment' and 'attachment_filename' must be present") # del data['attachment_filename'] # works but dirty return data
How to make it work?
EDIT
I managed to make it work by adding
del data['attachment_filename']
before validate
method return but that seems to be too “dirty”.
Advertisement
Answer
You should handle this behavior in serializer.save
method, for example, you can pop it from validated_data
like that:
def save(self, **kwargs): self.validated_data.pop("attachment_filename") return super().save(**kwargs)