diff --git a/examples/forms/app.py b/examples/forms/app.py index d77cadfe8..ec2e29bb1 100644 --- a/examples/forms/app.py +++ b/examples/forms/app.py @@ -101,7 +101,8 @@ class FileView(sqla.ModelView): form_args = { 'path': { 'label': 'File', - 'base_path': file_path + 'base_path': file_path, + 'allow_overwrite': False } } diff --git a/flask_admin/form/upload.py b/flask_admin/form/upload.py index 03a3819fd..c54e10693 100644 --- a/flask_admin/form/upload.py +++ b/flask_admin/form/upload.py @@ -51,12 +51,21 @@ def __call__(self, field, **kwargs): template = self.data_template if field.data else self.empty_template + if field.errors: + template = self.empty_template + + if field.data and isinstance(field.data, FileStorage): + value = field.data.filename + else: + value = field.data or '' + return HTMLString(template % { 'text': html_params(type='text', readonly='readonly', - value=field.data, + value=value, name=field.name), 'file': html_params(type='file', + value=value, **kwargs), 'marker': '_%s-delete' % field.name }) @@ -122,7 +131,7 @@ class FileUploadField(fields.StringField): def __init__(self, label=None, validators=None, base_path=None, relative_path=None, namegen=None, allowed_extensions=None, - permission=0o666, + permission=0o666, allow_overwrite=True, **kwargs): """ Constructor. @@ -154,6 +163,11 @@ class MyForm(BaseForm): :param allowed_extensions: List of allowed extensions. If not provided, will allow any file. + :param allow_overwrite: + Whether to overwrite existing files in upload directory. Defaults to `True`. + + .. versionadded:: 1.1.1 + The `allow_overwrite` parameter was added. """ self.base_path = base_path self.relative_path = relative_path @@ -161,6 +175,7 @@ class MyForm(BaseForm): self.namegen = namegen or namegen_filename self.allowed_extensions = allowed_extensions self.permission = permission + self._allow_overwrite = allow_overwrite self._should_delete = False @@ -188,6 +203,11 @@ def _is_uploaded_file(self, data): def pre_validate(self, form): if self._is_uploaded_file(self.data) and not self.is_file_allowed(self.data.filename): raise ValidationError(gettext('Invalid file extension')) + # Handle overwriting existing content + if not self._is_uploaded_file(self.data): + return + if self._allow_overwrite == False and os.path.exists(self._get_path(self.data.filename)): + raise ValidationError(gettext('File "%s" already exists.' % self.data.filename)) def process(self, formdata, data=unset_value): if formdata: @@ -253,6 +273,9 @@ def _save_file(self, data, filename): if not op.exists(op.dirname(path)): os.makedirs(os.path.dirname(path), self.permission | 0o111) + if self._allow_overwrite == False and os.path.exists(path): + raise ValueError(gettext('File "%s" already exists.' % path)) + data.save(path) return filename diff --git a/flask_admin/tests/test_form_upload.py b/flask_admin/tests/test_form_upload.py index d25901cb2..44c5c9f7f 100644 --- a/flask_admin/tests/test_form_upload.py +++ b/flask_admin/tests/test_form_upload.py @@ -40,6 +40,9 @@ def _remove_testfiles(): class TestForm(form.BaseForm): upload = form.FileUploadField('Upload', base_path=path) + class TestNoOverWriteForm(form.BaseForm): + upload = form.FileUploadField('Upload', base_path=path, allow_overwrite=False) + class Dummy(object): pass @@ -74,6 +77,7 @@ class Dummy(object): # Check delete with app.test_request_context(method='POST', data={'_upload-delete': 'checked'}): + my_form = TestForm(helpers.get_form_data()) ok_(my_form.validate()) @@ -83,6 +87,24 @@ class Dummy(object): ok_(not op.exists(op.join(path, 'test2.txt'))) + # Check overwrite + _remove_testfiles() + my_form_ow = TestNoOverWriteForm() + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hullo'), 'test1.txt')}): + my_form_ow = TestNoOverWriteForm(helpers.get_form_data()) + + ok_(my_form_ow.validate()) + my_form_ow.populate_obj(dummy) + eq_(dummy.upload, 'test1.txt') + ok_(op.exists(op.join(path, 'test1.txt'))) + + with app.test_request_context(method='POST', data={'upload': (BytesIO(b'Hullo'), 'test1.txt')}): + my_form_ow = TestNoOverWriteForm(helpers.get_form_data()) + + ok_(not my_form_ow.validate()) + + _remove_testfiles() + def test_image_upload_field(): app = Flask(__name__)