Commit 39528b9e authored by Serge S. Koval's avatar Serge S. Koval

Added FileUploadField and ImageUploadField examples

parent 7962a8ee
......@@ -18,3 +18,5 @@ venv
.coverage
__pycache__
examples/sqla-inline/static
examples/file/files
examples/forms/files
Examples of some Flask-Admin custom WTForms fields and widgets.
\ No newline at end of file
import os
import os.path as op
from flask import Flask, url_for
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.event import listens_for
from jinja2 import Markup
from flask.ext.admin import Admin, form
from flask.ext.admin.contrib import sqla
# Create application
app = Flask(__name__, static_folder='files')
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.sqlite'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Create directory for file fields to use
file_path = op.join(op.dirname(__file__), 'files')
try:
os.mkdir(file_path)
except OSError:
pass
# Create models
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(64))
path = db.Column(db.Unicode(128))
def __unicode__(self):
return self.name
class Image(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(64))
path = db.Column(db.Unicode(128))
def __unicode__(self):
return self.name
# Delete hooks for models, delete files if models are getting deleted
@listens_for(File, 'after_delete')
def del_file(mapper, connection, target):
if target.path:
try:
os.remove(op.join(file_path, target.path))
except OSError:
# Don't care if was not deleted because it does not exist
pass
@listens_for(Image, 'after_delete')
def del_image(mapper, connection, target):
if target.path:
# Delete image
try:
os.remove(op.join(file_path, target.path))
except OSError:
pass
# Delete thumbnail
try:
os.remove(op.join(file_path,
form.thumbgen_filename(target.path)))
except OSError:
pass
# Administrative views
class FileView(sqla.ModelView):
# Override form field to use Flask-Admin FileUploadField
form_overrides = {
'path': form.FileUploadField
}
# Pass additional parameters to 'path' to FileUploadField constructor
form_args = {
'path': {
'label': 'File',
'path': file_path
}
}
class ImageView(sqla.ModelView):
def _list_thumbnail(view, context, model, name):
if not model.path:
return ''
return Markup('<img src="%s">' % url_for('static',
filename=form.thumbgen_filename(model.path)))
column_formatters = {
'path': _list_thumbnail
}
# Alternative way to contribute field is to override it completely.
# In this case, Flask-Admin won't attempt to merge various parameters for the field.
form_extra_fields = {
'path': form.ImageUploadField('Image', path=file_path)
}
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
if __name__ == '__main__':
# Create admin
admin = Admin(app, 'Simple Models')
# Add views
admin.add_view(FileView(File, db.session))
admin.add_view(ImageView(Image, db.session))
# Create DB
db.create_all()
# Start app
app.run(debug=True)
......@@ -45,6 +45,7 @@ class FileUploadInput(object):
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
kwargs.setdefault('name', field.name)
template = self.data_template if field.data else self.empty_template
......@@ -72,6 +73,7 @@ class ImageUploadInput(object):
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
kwargs.setdefault('name', field.name)
args = {
'file': html_params(type='file',
......@@ -138,6 +140,8 @@ class FileUploadField(fields.TextField):
self._delete_file(field)
return
print field, type(self.data)
if isinstance(self.data, FileStorage):
if field:
self._delete_file(field)
......@@ -169,7 +173,7 @@ class ImageUploadField(FileUploadField):
raise Exception('PIL library was not found')
self.thumbnail_fn = thumbgen or thumbgen_filename
self.thumbnail_size = thumbnail_size
self.thumbnail_size = thumbnail_size or (128, 128, True)
self.endpoint = endpoint
self.image = None
......
import os
import os.path as op
from StringIO import StringIO
from nose.tools import eq_, ok_
from flask import Flask
from flask.ext.admin import form, helpers
def _create_temp():
path = op.join(op.dirname(__file__), 'tmp')
if not op.exists(path):
os.mkdir(path)
return path
def safe_delete(path, name):
try:
os.remove(op.join(path, name))
except:
pass
def test_upload_field():
app = Flask(__name__)
path = _create_temp()
def _remove_testfiles():
safe_delete(path, 'test1.txt')
safe_delete(path, 'test2.txt')
class TestForm(form.BaseForm):
upload = form.FileUploadField('Upload', path=path)
class Dummy(object):
pass
my_form = TestForm()
eq_(my_form.upload.path, path)
_remove_testfiles()
dummy = Dummy()
# Check upload
with app.test_request_context(method='POST', data={'upload': (StringIO('Hello World'), 'test1.txt')}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
eq_(dummy.upload, 'test1.txt')
ok_(op.exists(op.join(path, 'test1.txt')))
# Check replace
with app.test_request_context(method='POST', data={'upload': (StringIO('Hello World'), 'test2.txt')}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
eq_(dummy.upload, 'test2.txt')
ok_(not op.exists(op.join(path, 'test1.txt')))
ok_(op.exists(op.join(path, 'test2.txt')))
# Check delete
with app.test_request_context(method='POST', data={'_upload-delete': 'checked'}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
ok_(not op.exists(op.join(path, 'test2.txt')))
def test_image_upload_field():
app = Flask(__name__)
path = _create_temp()
def _remove_testimages():
safe_delete(path, 'test1.png')
safe_delete(path, 'test1_thumb.jpg')
safe_delete(path, 'test2.png')
safe_delete(path, 'test2_thumb.jpg')
class TestForm(form.BaseForm):
upload = form.ImageUploadField('Upload', path=path, thumbnail_size=(100, 100, True))
class TestNoResizeForm(form.BaseForm):
upload = form.ImageUploadField('Upload', path=path)
class Dummy(object):
pass
my_form = TestForm()
eq_(my_form.upload.path, path)
eq_(my_form.upload.endpoint, 'static')
_remove_testimages()
dummy = Dummy()
# Check upload
with file(op.join(op.dirname(__file__), 'data', 'copyleft.png'), 'rb') as fp:
with app.test_request_context(method='POST', data={'upload': (fp, 'test1.png')}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
eq_(dummy.upload, 'test1.png')
ok_(op.exists(op.join(path, 'test1.png')))
ok_(op.exists(op.join(path, 'test1_thumb.jpg')))
# Check replace
with file(op.join(op.dirname(__file__), 'data', 'copyleft.png'), 'rb') as fp:
with app.test_request_context(method='POST', data={'upload': (fp, 'test2.png')}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
eq_(dummy.upload, 'test2.png')
ok_(op.exists(op.join(path, 'test2.png')))
ok_(op.exists(op.join(path, 'test2_thumb.jpg')))
ok_(not op.exists(op.join(path, 'test1.png')))
ok_(not op.exists(op.join(path, 'test1_thumb.jpg')))
# Check delete
with app.test_request_context(method='POST', data={'_upload-delete': 'checked'}):
my_form = TestForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
ok_(not op.exists(op.join(path, 'test2.png')))
ok_(not op.exists(op.join(path, 'test2_thumb.jpg')))
# Check upload no-resize
with file(op.join(op.dirname(__file__), 'data', 'copyleft.png'), 'rb') as fp:
with app.test_request_context(method='POST', data={'upload': (fp, 'test1.png')}):
my_form = TestNoResizeForm(helpers.get_form_data())
ok_(my_form.validate())
my_form.populate_obj(dummy)
eq_(dummy.upload, 'test1.png')
ok_(op.exists(op.join(path, 'test1.png')))
ok_(op.exists(op.join(path, 'test1_thumb.jpg')))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment