Уведомления

Группа в Telegram: @pythonsu

#1 Окт. 30, 2014 00:23:11

zillion
Зарегистрирован: 2012-08-24
Сообщения: 20
Репутация: +  0  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

Всем привет.

Задача: На сайте необходимо сделать загрузку изображения и xml файла, модель и форма приведены ниже:

Модель:

class Image(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    image_url = db.Column(db.String(255))
    image_file = db.Column(db.String(255))
    xml_url = db.Column(db.String(255))
    xml_file = db.Column(db.String(255))

Форма:
class ImageForm(Form):
    image_url = StringField(validators=[URL()])
    image_file = FileField(validators=[FileRequired(), FileAllowed(['jpg', 'png'])])
    xml_url = StringField(validators=[URL()])
    xml_file = FileField(validators=[FileAllowed(['xml'])])

При добавлении важно что бы было указано одно из полей, _url или _file, для image и xml, так же могут быть указаны все поля. Как лучше организовать такую валидацию, и как обойти то, что при отправке формы поля с файлами очищаются и нужно выбирать заново файл?

Заранее спасибо!

Отредактировано zillion (Окт. 30, 2014 00:24:00)

Офлайн

#2 Окт. 30, 2014 00:47:16

4kpt_II
От: Харьков
Зарегистрирован: 2013-10-24
Сообщения: 999
Репутация: +  58  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

По организации сложно сказать. Какая-то специфическая у Вас задача. Я бы просто пропускал все без валидаторов и уже внутри вьюхи обрабатывал полученные результаты. Я сомневаюсь, что можно написать валидатор, который бы проверял заполнение другого поля да и еще его валидность. Точнее написать можно все, что угодно, но это будет адский оверхед. Можете написать свой механизм валидации конкретно для этой формы, но он ничем не будет отличаться от простого анализа, который я предлагаю реализовать во вьюхе-обработчике.

С файлами не знаю как, но для повтора данных после не валидной формы просто вставляю нужные данные внутри шаблона…

P.S. Гляньте в сторону sqlalchemy-wtforms. Не для этой задачи, а просто для общего развития

Отредактировано 4kpt_II (Окт. 30, 2014 00:47:33)

Офлайн

#3 Ноя. 2, 2014 00:31:16

zillion
Зарегистрирован: 2012-08-24
Сообщения: 20
Репутация: +  0  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

4kpt_II, спасибо за подсказку, для добавления сделал так:

class ImageForm(Form):
    image_url = StringField()
    image = FileField()
    xml_url = StringField()
    xml = FileField()
    def validate(self):
        if self.image.data:
            self.image.validators.append(FileAllowed(['apk']))
        else:
            self.image_url.validators.extend([DataRequired(), URL()])
        if self.xml.data:
            self.xml.validators.append(FileAllowed(['xml']))
        else:
            self.xml_url.validators.extend([DataRequired(), URL()])
        return super(ImageForm, self).validate()
Все работает как требуется, но остался небольшой затык с редактированием модели, а именно подставлением файлов при валидации…

Офлайн

#4 Ноя. 2, 2014 00:47:52

4kpt_II
От: Харьков
Зарегистрирован: 2013-10-24
Сообщения: 999
Репутация: +  58  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

Каким “подставлением”? Что Вы имеете ввиду?
Догадался. Если форма не валидна, чтобы опять файл был в указанном поле и не надо его было снова выбирать?

Отредактировано 4kpt_II (Ноя. 2, 2014 00:48:53)

Офлайн

#5 Ноя. 2, 2014 01:01:10

zillion
Зарегистрирован: 2012-08-24
Сообщения: 20
Репутация: +  0  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

4kpt_II
Каким “подставлением”? Что Вы имеете ввиду?Догадался. Если форма не валидна, чтобы опять файл был в указанном поле и не надо его было снова выбирать?

Да, не именно в html формы (так как это невозможно), а при валидации нужно указать image и xml поля файлов из модели, если они указаны. Инициализация через form = AppForm(obj=application) работает только с текстовыми полями.

Офлайн

#6 Ноя. 2, 2014 20:36:47

zillion
Зарегистрирован: 2012-08-24
Сообщения: 20
Репутация: +  0  -
Профиль   Отправить e-mail  

[Flask] WTForms и файлы

Вроде разобрался. сделал следующим образом:

@app.route('/<img_id>/edit', methods=['GET', 'POST'])
def edit_image(img_id):
    image = Image.query.filter_by(id=img_id).first_or_404()
    form = ImageForm(obj=image)
    if image.image_file:
        form.image_file.data = FileStorage(filename=image.image_file)
    if image.xml_file:
        form.xml_file.data = FileStorage(filename=image.xml_file)
    if form.validate_on_submit():
        image.image_url = form.image_url.data
        image.xml_url = form.xml_url.data
        if form.apk.data:
            image_file = form.data['image_file']
            image_filename = get_file_path('images', image_file.filename)
            image_file.save(os.path.join(app.config.get('MEDIA_ROOT'), image_filename))
            image.img_file = image_filename
        if form.xml_file.data:
            xml_file = form.data['xml_file']
            xml_filename = get_file_path('sitemaps', xml_file.filename)
            xml_file.save(os.path.join(app.config.get('MEDIA_ROOT'), xml_filename))
            image.xml_file = xml_filename
        db.session.commit()
        return redirect(url_for('app.images))
    return render_template('images/edit.html', form=form)

Отредактировано zillion (Ноя. 2, 2014 20:37:31)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version