使用Flask-admin在PDF中存储PDF文件

时间:2015-11-15 16:40:52

标签: python mongodb object flask-sqlalchemy flask-admin

我可以使用Flask-Admin将PDF文件作为对象或blob存储在数据库中吗? 我在文档中找不到任何参考。

感谢。 干杯

2 个答案:

答案 0 :(得分:9)

下面是一个自包含通过Flask-Admin直接在blob字段中存储文件的示例。错误检查很少,但它应该让你朝着正确的方向前进。

代码的重要部分:

class BlobMixin(object):
    mimetype = db.Column(db.Unicode(length=255), nullable=False)
    filename = db.Column(db.Unicode(length=255), nullable=False)
    blob = db.Column(db.LargeBinary(), nullable=False)
    size = db.Column(db.Integer, nullable=False)

BlobMixin类定义了哪些字段与blob数据一起存储,通常它可用于携带其他信息,如文件大小,mime类型和原始上载文件的文件名。

class Image(db.Model, BlobMixin):
    __tablename__ = 'images'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(length=255), nullable=False, unique=True)

    def __unicode__(self):
        return u"name : {name}; filename : {filename})".format(name=self.name, filename=self.filename)

Image class是存储blob的数据库表(通过BlobMixin)。在这个例子中,我们给每个图像一个唯一的名称,该名称与上传的文件名无关。

BlobUploadField(fields.StringField)几乎是Flask-Admin的FileUploadField类的副本。但是有一些重要的区别 - 我们需要知道我们用来存储文件大小,mime类型和原始文件名的字段。它们通过构造函数传递,并在def populate_obj(self, obj, name)方法中使用。

ImageView(ModelView)是一个简单的Flask-Admin视图。请注意如何在form_extra_fields中定义blob字段。我们正在构建一个BlobUploadField并传入允许的文件扩展名列表,大小字段名称,文件名字段名称和mime类型字段名称。字段的名称(大小,文件名和mimetype)直接取自BlobMixin类字段名称。

form_extra_fields = {'blob': BlobUploadField(
    label='File',
    allowed_extensions=['pdf', 'doc', 'docx', 'xls', 'xlsx', 'png', 'jpg', 'jpeg', 'gif'],
    size_field='size',
    filename_field='filename',
    mimetype_field='mimetype'
)}

我已经使用适当的列格式化程序将下载链接列添加到列表视图中,以便您可以单击该链接并下载该文件。

下面的代码使用Python 2.7.9,Flask 0.10.0,Flask-Admin 1.1.0和Flask-SQLAlchemy 2.0进行测试。使用SQLite内存数据库,因此在烧瓶应用程序关闭时数据将丢失。

import io
from gettext import gettext
from flask import Flask, send_file
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from flask.ext.sqlalchemy import SQLAlchemy
from markupsafe import Markup
from werkzeug.datastructures import FileStorage
from wtforms import ValidationError, fields
from wtforms.validators import required
from wtforms.widgets import HTMLString, html_params, FileInput

try:
    from wtforms.fields.core import _unset_value as unset_value
except ImportError:
    from wtforms.utils import unset_value

app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)


def build_db():
    db.drop_all()
    db.create_all()


class BlobMixin(object):
    mimetype = db.Column(db.Unicode(length=255), nullable=False)
    filename = db.Column(db.Unicode(length=255), nullable=False)
    blob = db.Column(db.LargeBinary(), nullable=False)
    size = db.Column(db.Integer, nullable=False)


class Image(db.Model, BlobMixin):
    __tablename__ = 'images'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(length=255), nullable=False, unique=True)

    def __unicode__(self):
        return u"name : {name}; filename : {filename})".format(name=self.name, filename=self.filename)


class BlobUploadField(fields.StringField):

    widget = FileInput()

    def __init__(self, label=None, allowed_extensions=None, size_field=None, filename_field=None, mimetype_field=None, **kwargs):

        self.allowed_extensions = allowed_extensions
        self.size_field = size_field
        self.filename_field = filename_field
        self.mimetype_field = mimetype_field
        validators = [required()]

        super(BlobUploadField, self).__init__(label, validators, **kwargs)

    def is_file_allowed(self, filename):
        """
            Check if file extension is allowed.

            :param filename:
                File name to check
        """
        if not self.allowed_extensions:
            return True

        return ('.' in filename and
                filename.rsplit('.', 1)[1].lower() in
                map(lambda x: x.lower(), self.allowed_extensions))

    def _is_uploaded_file(self, data):
        return (data and isinstance(data, FileStorage) and data.filename)

    def pre_validate(self, form):
        super(BlobUploadField, self).pre_validate(form)
        if self._is_uploaded_file(self.data) and not self.is_file_allowed(self.data.filename):
            raise ValidationError(gettext('Invalid file extension'))

    def process_formdata(self, valuelist):
        if valuelist:
            data = valuelist[0]
            self.data = data

    def populate_obj(self, obj, name):

        if self._is_uploaded_file(self.data):

            _blob = self.data.read()

            setattr(obj, name, _blob)

            if self.size_field:
                setattr(obj, self.size_field, len(_blob))

            if self.filename_field:
                setattr(obj, self.filename_field, self.data.filename)

            if self.mimetype_field:
                setattr(obj, self.mimetype_field, self.data.content_type)


class ImageView(ModelView):

    column_list = ('name', 'size', 'filename', 'mimetype', 'download')
    form_columns = ('name', 'blob')

    form_extra_fields = {'blob': BlobUploadField(
        label='File',
        allowed_extensions=['pdf', 'doc', 'docx', 'xls', 'xlsx', 'png', 'jpg', 'jpeg', 'gif'],
        size_field='size',
        filename_field='filename',
        mimetype_field='mimetype'
    )}

    def _download_formatter(self, context, model, name):
        return Markup("<a href='{url}' target='_blank'>Download</a>".format(url=self.get_url('download_blob', id=model.id)))

    column_formatters = {
        'download': _download_formatter,
    }


# download route

@app.route("/download/<int:id>", methods=['GET'])
def download_blob(id):
    _image = Image.query.get_or_404(id)
    return send_file(
        io.BytesIO(_image.blob),
        attachment_filename=_image.filename,
        mimetype=_image.mimetype
    )

# Create admin
admin = Admin(app, name='Admin', url='/')
admin.add_view(ImageView(model=Image, session=db.session, category='Database', name='Images'))


@app.before_first_request
def first_request():
    build_db()

if __name__ == '__main__':
    app.run(debug=True, port=7777)

答案 1 :(得分:-2)

Flask-Admin不存储任何内容。它只是进入底层存储的窗口。

所以,是的,只要数据库的引擎支持blob类型,您就可以在Flask-Admin应用中拥有blob个字段。

如果需要进一步说明,Flask-Admin不是数据库。它是数据库的接口。在flask-admin应用程序中,您连接到预先存在的数据库。这可能是一个sqlite数据库,PostGresSQL,MySQL,MongoDB或任何各种数据库。