[PYTHON] How to make WTForms TextArea correspond to file drop

Background

When I implemented a project in Flask, In the public key registration form (Text Area) that led to "Copy & Paste", There was an end user who dropped a file. I thought that there were people who did that, but I investigated if I could manage it ... StackOverflow: Load Textfile via Drag and Drop on Textarea I found that.

I found that I could do simple JavaScript unexpectedly, so I implemented it in Flask.

Flask First, define the public key registration form in Flask as the FORM class RegistForm. Public key validation uses the sshpubkeys library, which is not the main subject.

It is a test application that just receives and displays the verified data of the form data.

import os
from flask import (
    Flask,
    request,
    render_template,
    redirect,
    url_for,
)

from flask_wtf import FlaskForm
from wtforms import TextAreaField, SubmitField, ValidationError
from wtforms.validators import InputRequired
from sshpubkeys import SSHKey, InvalidKeyError, MalformedDataError

class RegistForm(FlaskForm):
    pubkey = TextAreaField("PublicKey",validators=[InputRequired()])
    submit = SubmitField("OK")
    reset = SubmitField("RESET")

    def validate_pubkey(self, field):
        if field.data.find('\n') >1:
            raise ValidationError("Too Many Publickey")

        sshpubkey = SSHKey(field.data, strict=True)
        if sshpubkey.key_type != b"ssh-rsa":
            raise ValidationError("Invalid Key Type")

        try:
            sshpubkey.parse()
        except InvalidKeyError as err:
            raise ValidationError("Invalid PublicKey")
        except MalformedDataError as err:
            raise ValidationError("Malformed key Error")
        except NotImplementedError as err:
            raise ValidationError("Undefined Error")


app = Flask(__name__, static_folder='static')
app.config['SECRET_KEY']='12345678901234567890'

@app.route("/", endpoint="register", methods=["GET", "POST"])
def register():
    form = RegistForm()
    if request.method == "GET":
        return render_template("test.html",
                               form=form,
                               endpoint="register")

    if form.validate_on_submit():
        return form.pubkey.data
    else:
        print(f'vaslidation error')
        return redirect(url_for("register"))


if __name__ == "__main__":
    app.run(
        host=os.getenv("APP_HOST", "localhost"),
        port=os.getenv("APP_PORT", 8080),
        debug=True,
    )

JavaScript

Prepare the following functions in static / functions.js.

function dropfile(file) {
    var reader = new FileReader();
    reader.onload = function(e) {
        notepad.value = e.target.result.replace(/(?:\r\n|\r|\n)/g, '');
    };
    reader.readAsText(file, "UTF-8");
};

notepad.ondrop = function(e) {
    e.preventDefault();
    var file = e.dataTransfer.files[0];
    dropfile(file);
};

I'm using a DataTransfer object, but not all browsers publish the object. If you have a modern browser, there should be no problem ...

HTML

Actually, this is the only thing that seems to be a knack. I have given the attribute id "notepad" to the field of the class defined in forms.py.

{% extends "base.html" %}

{% block contents %}

<form action="{{ url_for( endpoint ) }}" method="post">
{{ form.hidden_tag() }}
{{ form.pubkey(id="notepad", raws=6, cols=80,
               placeholder="Copy&Past or Drag&Drop here!") }}
{{ form.submit }}
{{ form.reset }}
</form>
{% endblock contents %}

rows, cols, and placeholder can be given to render_kw in dictionary format in the class definition, but I think it's better not to define much related to drawing in the class.

render_kw={"rows": 6, "cols": 80, "placeholder": "Copy&Paste" }

This also has nothing to do with the main subject, but some people may be interested in it. For the time being, prepare such base.html.

{% from "_defaults.html" import render_htmlattribs, render_styles, render_scripts %}

{% block doc -%}
<!DOCTYPE html>
<html{% block html_attribs %} {{ render_htmlattribs() }}{% endblock html_attribs %}>
  {%- block html %}
  <head>
    {%- block head %}
    <title>{% block title %}{{ render_title() }}{% endblock title %}</title>

      {%- block metas %}
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta charset="UTF-8">
      {%- endblock metas %}
      {%- block favicon %} {%- endblock favicon %}

      {%- block styles %}
      {{ render_styles() }}
      {%- endblock styles %}

    {%- endblock head %}
  </head>
  <body{% block body_attribs %}{% endblock body_attribs %}>
    {%- block body %}

    {% block contents %} {% endblock contents %}

    {% block footer %}
      {{ render_footer() }}
    {%- endblock footer %}

    {% block scripts %}
      {{ render_scripts() }}
    {%- endblock scripts %}

    {%- endblock body %}
  </body>
  {%- endblock html %}
</html>
{% endblock doc -%}

The macro defined in _defaults.html looks like this: I'm using it all the time, so I try to load bootstrap and jquery, but You don't have to have this. All you have to do is load static / functions.js.

{% macro render_htmlattribs() -%}
lang="ja"
{%- endmacro %}

{% macro render_styles() %}
    <link href="/static/bootstrap-4.5.2/css/bootstrap.min.css" rel="stylesheet">
    <link href="/static/base.css" rel="stylesheet">
{% endmacro %}


{% macro render_scripts() %}
    <script src="/static/jquery-3.5.1.min.js"></script>
    <script src="/static/bootstrap-4.5.2/js/bootstrap.min.js"></script>
    <script src="/static/functions.js"></script>
{% endmacro %}

Reference material

Recommended Posts

How to make WTForms TextArea correspond to file drop
How to make a Japanese-English translation
How to make a slack bot
How to make a crawler --Advanced
How to make a recursive function
How to make a deadman's switch
[Blender] How to make a Blender plugin
[Blender] How to make Blender scripts multilingual
How to make a crawler --Basic
How to create a config file
How to make a command to read the configuration file with pyramid
How to make Word Cloud characters monochromatic
[Python] How to convert db file to csv
How to make Selenium as light as possible
[Python] How to make a class iterable
How to convert Python to an exe file
[Cocos2d-x] How to make Script Binding (Part 2)
How to make multi-boot USB (Windows 10 compatible)
How to make a Backtrader custom indicator
How to make a Pelican site map
[Cocos2d-x] How to make Script Binding (Part 1)
How to drop Google Docs in one folder in a .txt file with python
LINUX: How to make arrow keys correspond to 2,4,6,8 on a notebook without a numeric keypad
How to make a dialogue system dedicated to beginners
How to read a CSV file with Python 2/3
How to disguise a ZIP file as a PNG file
How to make an embedded Linux device driver (8)
How to make Spigot plugin (for Java beginners)
How to make an embedded Linux device driver (1)
How to make an embedded Linux device driver (4)
How to make multiple kernels selectable on Jupyter
How to create a JSON file in Python
How to make a dictionary with a hierarchical structure.
How to make an embedded Linux device driver (7)
[Python] How to read excel file with pandas
How to make an embedded Linux device driver (2)
How to make scrapy JSON output into Japanese
How to make an embedded Linux device driver (3)
How to make a QGIS plugin (package generation)
I read "How to make a hacking lab"
[Blender x Python] How to make an animation
How to make an embedded Linux device driver (6)
How to make Substance Painter Python plugin (Introduction)
[Blender x Python] How to make vertex animation
How to make an embedded Linux device driver (5)
How to make an embedded Linux device driver (10)
How to make Python faster for beginners [numpy]
How to read a file in a different directory
How to make Python Interpreter changes in Pycharm
How to make Linux compatible with Japanese keyboard
How to make an embedded Linux device driver (9)
Inspired by "How to make pure functional JavaScript"
How to make AWS rekognition recognize local image files
[Continued] Inspired by "How to make pure functional JavaScript"
How to make Yubico Yubikey recognized in Manjaro Linux
Explain in detail how to make sounds with python
How to make a shooting game with toio (Part 1)
Backtrader How to import an indicator from another file
How to measure mp3 file playback time with python
How to make unit tests Part.2 Class design for tests
How to convert JSON file to CSV file with Python Pandas