Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Base64FileField #149

Merged
merged 8 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include README.md
include LICENSE
include requirements.txt
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ DRF-EXTRA-FIELDS

Extra Fields for Django Rest Framework

**Possible breaking change in v3.1.0**: In this version we have changed file class used in `Base64FileField` from `ContentFile` to `SimpleUploadedFile` (you may see the change [here](https://github.com/Hipo/drf-extra-fields/pull/149/files#diff-5f77bcb61083cd9c026f6dfb3b77bf8fa824c45e620cdb7826ad713bde7b65f8L72-R85)).

[![Build Status](https://travis-ci.org/Hipo/drf-extra-fields.svg?branch=master)](https://travis-ci.org/Hipo/drf-extra-fields)
[![codecov](https://codecov.io/gh/Hipo/drf-extra-fields/branch/master/graph/badge.svg)](https://codecov.io/gh/Hipo/drf-extra-fields)
[![PyPI Version](https://img.shields.io/pypi/v/drf-extra-fields.svg)](https://pypi.org/project/drf-extra-fields)
Expand Down
41 changes: 27 additions & 14 deletions drf_extra_fields/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import uuid

from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import SimpleUploadedFile
from django.contrib.postgres import fields as postgres_fields
from django.utils.translation import gettext_lazy as _
from psycopg2.extras import DateRange, DateTimeTZRange, NumericRange
Expand All @@ -24,11 +24,12 @@
from rest_framework.utils import html
from drf_extra_fields import compat


DEFAULT_CONTENT_TYPE = "application/octet-stream"


class Base64FieldMixin(object):
EMPTY_VALUES = (None, "", [], (), {})

@property
def ALLOWED_TYPES(self):
raise NotImplementedError
Expand All @@ -41,10 +42,9 @@ def INVALID_FILE_MESSAGE(self):
def INVALID_TYPE_MESSAGE(self):
raise NotImplementedError

EMPTY_VALUES = (None, '', [], (), {})

def __init__(self, *args, **kwargs):
self.represent_in_base64 = kwargs.pop('represent_in_base64', False)
self.trust_provided_content_type = kwargs.pop("trust_provided_content_type", False)
self.represent_in_base64 = kwargs.pop("represent_in_base64", False)
super(Base64FieldMixin, self).__init__(*args, **kwargs)

def to_internal_value(self, base64_data):
Expand All @@ -53,26 +53,39 @@ def to_internal_value(self, base64_data):
return None

if isinstance(base64_data, str):
# Strip base64 header.
if ';base64,' in base64_data:
header, base64_data = base64_data.split(';base64,')
file_mime_type = None

# Strip base64 header, get mime_type from base64 header.
if ";base64," in base64_data:
header, base64_data = base64_data.split(";base64,")
if self.trust_provided_content_type:
file_mime_type = header.replace("data:", "")

# Try to decode the file. Return validation error if it fails.
try:
decoded_file = base64.b64decode(base64_data)
except (TypeError, binascii.Error, ValueError):
raise ValidationError(self.INVALID_FILE_MESSAGE)

# Generate file name:
file_name = self.get_file_name(decoded_file)

# Get the file name extension:
file_extension = self.get_file_extension(file_name, decoded_file)

if file_extension not in self.ALLOWED_TYPES:
raise ValidationError(self.INVALID_TYPE_MESSAGE)

complete_file_name = file_name + "." + file_extension
data = ContentFile(decoded_file, name=complete_file_name)
data = SimpleUploadedFile(
name=complete_file_name,
content=decoded_file,
content_type=file_mime_type
)

return super(Base64FieldMixin, self).to_internal_value(data)
raise ValidationError(_('Invalid type. This is not an base64 string: {}'.format(
type(base64_data))))

raise ValidationError(_("Invalid type. This is not an base64 string: {}".format(type(base64_data))))

def get_file_extension(self, filename, decoded_file):
raise NotImplementedError
Expand All @@ -87,10 +100,10 @@ def to_representation(self, file):
# empty base64 str rather than let the exception propagate unhandled
# up into serializers.
if not file:
return ''
return ""

try:
with open(file.path, 'rb') as f:
with open(file.path, "rb") as f:
return base64.b64encode(f.read()).decode()
except Exception:
raise IOError("Error encoding file")
Expand Down Expand Up @@ -156,6 +169,7 @@ class Base64FileField(Base64FieldMixin, FileField):
A django-rest-framework field for handling file-uploads through raw post data.
It uses base64 for en-/decoding the contents of the file.
"""

@property
def ALLOWED_TYPES(self):
raise NotImplementedError('List allowed file extensions')
Expand All @@ -168,7 +182,6 @@ def get_file_extension(self, filename, decoded_file):


class RangeField(DictField):

range_type = None

default_error_messages = dict(DictField.default_error_messages)
Expand Down
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@
with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme:
README = readme.read()

with open(os.path.join(os.path.dirname(__file__), 'requirements.txt')) as requirements_txt:
requirements = requirements_txt.read().strip().splitlines()

# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

setup(
name='drf-extra-fields',
version='3.0.4',
version='3.1.0',
packages=['drf_extra_fields',
'drf_extra_fields.runtests'],
include_package_data=True,
install_requires=['Django >= 2.2', 'djangorestframework >= 3.9.1'],
extras_require={
"Base64ImageField": ["Pillow >= 6.2.1"],
},
Expand All @@ -26,6 +28,7 @@
author_email='[email protected]',
url='https://github.com/Hipo/drf-extra-fields',
python_requires=">=3.5",
install_requires=requirements,
classifiers=[
'Environment :: Web Environment',
'Framework :: Django',
Expand Down