У вас включен AdBlock или иной блокировщик рекламы.

Пожалуйста, отключите его, доход от рекламы помогает развитию сайта и появлению новых статей.

Спасибо за понимание.

В другой раз
DevGang блог о програмировании
Авторизоваться

Flask Admin: загрузка файлов и обработка формы в модели

Мне довольно часто требуется кастомизировать базовую модель Flask Admin для того чтобы например загружать изображения. В интернете есть довольно много примеров как сделать загрузку файлов через `flask_admin.form.upload`, но он не дает полного контроля над происходящим и если потребуется сделать что-то более сложное, придется импровизировать.

Поиграв с дебагером и почитав документацию, я пришел к выводу, что в моем случае хорошо подойдет 2 функции модели edit_form и create_form. Они вызываются при создании формы из реквеста, и до того как произойдет валидация их можно переопределить.

Начнем

Есть модель:

class StorageModel(db.Model):
    __tablename__ = 'storage'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))
    path = db.Column(db.Unicode(128))
    type = db.Column(db.Unicode(3))
    create_date = db.Column(db.DateTime, default=datetime.datetime.now)

Создаем view и переопределяем обработчики форм

from flask_admin.contrib import sqla
from flask_admin import form

import random
import os


class StorageAdminModel(sqla.ModelView):
    form_extra_fields = {
        'file': form.FileUploadField('file')
    }

    def _change_path_data(self, _form):
        try:
            storage_file = _form.file.data

            if storage_file is not None:
                hash = random.getrandbits(128)
                ext = storage_file.filename.split('.')[-1]
                path = '%s.%s' % (hash, ext)

                storage_file.save(
                    os.path.join(app.config['STORAGE'], path)
                )

                _form.name.data = _form.name.data or storage_file.filename
                _form.path.data = path
                _form.type.data = ext

                del _form.file

        except Exception as ex:
            pass

        return _form

    def edit_form(self, obj=None):
        return self._change_path_data(
            super(StorageAdminModel, self).edit_form(obj)
        )

    def create_form(self, obj=None):
        return self._change_path_data(
            super(StorageAdminModel, self).create_form(obj)
        )


admin.add_view(StorageAdminModel(StorageModel, db.session))
  • edit_form - вызывается при редактировании записи
  • create_form - соответственно при создании

Мы перехватываем формирование формы и обрабатываем данные до того как они попадут в модель. Здесь важно отметить, ключи формы должны соответствовать вашей модели данных в базе данных, чтобы не свалились ошибки. 

Через form_extra_fields мы добавляем новое поле file и задаем ему тип FileUploadField. Это добавит в форму новый field с возможностью загрузки файла. После обработки данных удаляем его из формы del _form.file

Вот вообщем и все, мы получили дополнительные данные, записали их в нужные нам поля.

В результате у вас должно получится как ниже на скриншоте:

Бонус

На списке объектов, было бы неплохо сразу видеть то что мы загрузи, если это картинка, показать картинку, если это запись, дать возможность ее воспроизвести. С этим нам поможет column_formatters. Добавим в наш view еще немного магии

def _list_thumbnail(view, context, model, name):
    if not model.path:
        return ''

    url = url_for('static', filename=os.path.join('storage/', model.path))

    if model.type in ['jpg', 'jpeg', 'png', 'svg', 'gif']:
        return Markup('<img src="%s" width="100">' % url)

    if model.type in ['mp3']:
        return Markup('<audio controls="controls"><source src="%s" type="audio/mpeg" /></audio>' % url)

column_formatters = {
    'path': _list_thumbnail
}

И смотрим что получилось:

Весь файл целиком:

from app import app
from app import admin
from app import db

from app.models.StorageModel import StorageModel

from jinja2 import Markup
from flask import url_for
from flask_admin import form

from flask_admin.contrib import sqla

import random
import os


class StorageAdminModel(sqla.ModelView):
    def _list_thumbnail(view, context, model, name):
        if not model.path:
            return ''

        url = url_for('static', filename=os.path.join('storage/', model.path))

        if model.type in ['jpg', 'jpeg', 'png', 'svg', 'gif']:
            return Markup('' % url)

        if model.type in ['mp3']:
            return Markup('' % url)

    column_formatters = {
        'path': _list_thumbnail
    }

    form_extra_fields = {
        'file': form.FileUploadField('file')
    }

    def _change_path_data(self, _form):
        try:
            storage_file = _form.file.data

            if storage_file is not None:
                hash = random.getrandbits(128)
                ext = storage_file.filename.split('.')[-1]
                path = '%s.%s' % (hash, ext)

                storage_file.save(
                    os.path.join(app.config['STORAGE'], path)
                )

                _form.name.data = _form.name.data or storage_file.filename
                _form.path.data = path
                _form.type.data = ext

                del _form.file

        except Exception as ex:
            pass

        return _form

    def edit_form(self, obj=None):
        return self._change_path_data(
            super(StorageAdminModel, self).edit_form(obj)
        )

    def create_form(self, obj=None):
        return self._change_path_data(
            super(StorageAdminModel, self).create_form(obj)
        )


admin.add_view(StorageAdminModel(StorageModel, db.session))
#Flask
Присоеденяйся в тусовку

Будь всегдя вкурсе новостей из мира IT