forked from kaleidos/django-validated-file
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
138 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
from .file import * | ||
from .image import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# -*- coding: utf-8 -*- | ||
__all__ = ['ConstrainedFileField'] | ||
|
||
import os | ||
|
||
from django import forms | ||
from django.conf import settings | ||
from django.db import models | ||
from django.template.defaultfilters import filesizeformat | ||
from django.utils.translation import ugettext_lazy as _ | ||
|
||
|
||
class ConstrainedFileField(models.FileField): | ||
""" | ||
A FielField with additional constraints. Namely, the file size and type can be restricted. If | ||
using the types, the magic library is required. Setting neither a file size nor type behaves | ||
like a regular FileField. | ||
Parameters | ||
---------- | ||
content_types : list of str | ||
List containing allowed content_types. Example: ['application/pdf', 'image/jpeg'] | ||
max_upload_size : int | ||
Maximum file size allowed for upload, in bytes | ||
1 MB - 1048576 B - 1024**2 B - 2**20 B | ||
2.5 MB - 2621440 B | ||
5 MB - 5242880 B | ||
10 MB - 10485760 B | ||
20 MB - 20971520 B | ||
33 MiB - 2**25 B | ||
50 MB - 5242880 B | ||
100 MB 104857600 B | ||
250 MB - 214958080 B | ||
500 MB - 429916160 B | ||
1 GiB - 1024 MiB - 2**30 B | ||
js_checker : bool | ||
Add a javascript file size checker to the form field | ||
mime_lookup_length : int | ||
See Also | ||
-------- | ||
Based on https://github.com/kaleidos/django-validated-file/blob/master/validatedfile/fields.py | ||
With inspiration from http://stackoverflow.com/a/9016664 | ||
""" | ||
|
||
description = _("A file field with constraints on size and/or type") | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.max_upload_size = kwargs.pop("max_upload_size", 0) | ||
assert isinstance(self.max_upload_size, int) and self.max_upload_size >= 0 | ||
self.content_types = kwargs.pop("content_types", []) | ||
self.mime_lookup_length = kwargs.pop("mime_lookup_length", 4096) | ||
assert isinstance(self.mime_lookup_length, int) and self.mime_lookup_length >= 0 | ||
self.js_checker = kwargs.pop("js_checker", False) | ||
|
||
super(ConstrainedFileField, self).__init__(*args, **kwargs) | ||
|
||
def clean(self, *args, **kwargs): | ||
data = super(ConstrainedFileField, self).clean(*args, **kwargs) | ||
|
||
if self.max_upload_size and data.size > self.max_upload_size: | ||
# Ensure no one bypasses the js checker | ||
raise forms.ValidationError( | ||
_('File size exceeds limit: %(current_size)s. Limit is %(max_size)s.') % | ||
{'max_size': filesizeformat(self.max_upload_size), | ||
'current_size': filesizeformat(data.size)}) | ||
|
||
if self.content_types: | ||
import magic | ||
file = data.file | ||
uploaded_content_type = getattr(file, 'content_type', '') | ||
|
||
# magic_file_path used only for Windows. | ||
magic_file_path = getattr(settings, "MAGIC_FILE_PATH", None) | ||
if magic_file_path and os.name == 'nt': | ||
mg = magic.Magic(mime=True, magic_file=magic_file_path) | ||
else: | ||
mg = magic.Magic(mime=True) | ||
content_type_magic = mg.from_buffer(file.read(self.mime_lookup_length)) | ||
file.seek(0) | ||
|
||
# Prefer mime-type from magic over mime-type from http header | ||
if uploaded_content_type != content_type_magic: | ||
uploaded_content_type = content_type_magic | ||
|
||
if uploaded_content_type not in self.content_types: | ||
raise forms.ValidationError( | ||
_('Unsupported file type: %(type)s. Allowed types are %(allowed)s.') % | ||
{'type': content_type_magic, | ||
'allowed': self.content_types}) | ||
return data | ||
|
||
def formfield(self, **kwargs): | ||
""" | ||
Usual Form for a django.models.FileField with optional javascript file size | ||
checker. Can thus be customized as any other Form for a django.models.FileField. | ||
Parameters | ||
---------- | ||
kwargs | ||
will be passed to super().formfield() | ||
Returns | ||
------- | ||
django.forms.FileField | ||
""" | ||
formfield = super(ConstrainedFileField, self).formfield(**kwargs) | ||
if self.js_checker: | ||
formfield.widget.attrs.update( | ||
{'onchange': 'validateFileSize(this, 0, %d);' % (self.max_upload_size,)}) | ||
return formfield | ||
|
||
def deconstruct(self): | ||
name, path, args, kwargs = super(ConstrainedFileField, self).deconstruct() | ||
if self.max_upload_size: | ||
kwargs["max_upload_size"] = self.max_upload_size | ||
if self.content_types: | ||
kwargs["content_types"] = self.content_types | ||
if self.mime_lookup_length: | ||
kwargs["mime_lookup_length"] = self.mime_lookup_length | ||
if self.js_checker: | ||
kwargs["js_checker"] = self.js_checker | ||
return name, path, args, kwargs | ||
|
||
def __str__(self): | ||
if hasattr(self, 'model'): | ||
return super(ConstrainedFileField).__str__() | ||
else: | ||
return self.__class__.__name__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters