Home > Software engineering >  How to save form data in flask with Vue.js
How to save form data in flask with Vue.js

Time:11-26

I'm working on a simple assignment where I have to use tools I'm not that experienced with. I would need to save the form data somehow(to a text file, which is the best option) or at least serialize it to JSON so that I can dump it to the screen later. Could someone please at least point me in the direction of where to look. Some sort of starting point that I can work off of. So far I have this code, but it doesn't save the data anywhere

HTML

<form  @submit="add" >

  <label >Nickname</label><br>

  <input type="text" v-model="nickname" ref="new"  autofocus name="nick" required><br>
  <span  v-if="!nickname.match(/^[A-Za-z0-9] $/) || nickname.length <= 2 || nickname.length >= 8">  {{message}}</span>

  <br></br>

  <label for="lname" required>Can you swim</label><br>
  <input type="radio" value="Yes" v-model="can_Swim" id="lname" name="je_plavec" required><br><br>

  <label >Your partner</label><br>

  <input type="text" v-model="partner" ref="new"  autofocus name="partner" >
  <br>
  <span  v-if="!partner.match(/^[A-Za-z0-9] $/) || partner.length <= 2 || partner.length >= 20"> {{message}}</span>
  <br><br>

  <section>
    <input  type="submit" value="submit">
    <a href="/index.html">

      <span>Back</span>

    </a>
  </section>

</form>
<transition-group name="student"
tag="ul" >
  <li  v-for="(student, index) in students" :key="index">

    <article : >
     {{student.nickname}}
      <br>
      <span>

        {{student.partner}}
      </span>
      <br>

       <span>

        {{student.can_Swim}}
      </span>
      <br>


    </article>
  </li>
</transition-group>

Vue.js code:

<script>

  const vueApp  =  new Vue({
  el: '#app',
  data: {
    nickname:'',
    can_Swim:'',
    partner:'',
    message:"error",
    students:[]

  },

  methods:{
    add(e){
      e.preventDefault()
      this.students.push({
                    nickname:this.nickname,
                    can_Swim:this.can_Swim,
                    partner:this.partner
                }
                )
                this.nickname = ''
                this.partner = ''
                this.can_Swim = false,

                this.$refs.new.focus()
    },

  },


})

</script>


Python

@app.route("/registrace", methods=['GET'])
def reg_form():

    return render_template('registration.html')


I would like to save data like that http://jsfiddle.net/exftm3b8/1/

CodePudding user response:

My example uses a database to store the entries.
To realize this, the Flask SQLAlchemy, Flask-Marshmallow, Marshmallow-SQLAlchemy and Webargs are used.
These are installed using this command.

pip install flask-sqlalchemy flask-marshmallow marshmallow-sqlalchemy webargs

Once all entries have been made and the Add button is pressed, they are sent to the server via AJAX in JSON format. Here all data is automatically deserialized, validated again and added to the database. The database entry is then converted back into JSON and sent back to the client, where it is added to the list of students.

If the page is reloaded, all entries are loaded from the database, serialized and sent to the client.

Flask (./app.py)
from flask import Flask, jsonify
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from webargs import fields, validate, ValidationError
from webargs.flaskparser import use_args

app = Flask(__name__)
# Database configuration
app.config.from_mapping(
    SQLALCHEMY_DATABASE_URI='sqlite:///demo.db'
)
db = SQLAlchemy(app)
ma = Marshmallow(app)

# Database model with all required columns
class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False, unique=True)
    partner = db.Column(db.String, nullable=False)
    can_swim = db.Column(db.Boolean, nullable=False, default=False)

# Validation of the uniqueness of the name.
def validate_uniqueness(val):
    # If a student with that name already exists, throw an error.
    if Student.query.filter_by(name=val).first():
        raise ValidationError('Name already exists.')

# Scheme for serialization and validation
class StudentSchema(ma.SQLAlchemyAutoSchema):
    name = ma.String(required=True, 
        validate=[
            validate.Length(min=2, max=8), 
            validate.Regexp(r'^[A-Za-z] [0-9]*$'), 
            validate_uniqueness
        ]
    )
    partner = ma.String(required=True, 
        validate=[
            validate.Length(min=2, max=8), 
            validate.Regexp(r'^[A-Za-z] [0-9]*$')
        ]
    )
    can_swim = ma.Boolean(required=True)

    class Meta:
        model = Student
        load_instance = True

# Creation of the database. 
# This can also be done via the flask shell.
with app.app_context():
    db.drop_all()
    db.create_all()

# Deliver the VueJS application as a static page.
@app.route('/')
def index():
    return app.send_static_file('index.html')

# Creation of a new database entry
@app.post('/students/new')
@use_args(StudentSchema(), location='json')
def students_create(student):
    # Save the data to the database.
    db.session.add(student)
    db.session.commit()
    # Convert the data to JSON.
    student_schema = StudentSchema()
    student_data = student_schema.dump(student)
    return jsonify(student_data)

# Query and delivery of all existing database entries.
@app.route('/students')
def students():
    # Query all students from the database.
    students = Student.query.all()
    # Convert the data to JSON.
    student_schema = StudentSchema(many=True)
    student_data = student_schema.dump(students)
    return jsonify(student_data)

# Error handler for failed validation.
@app.errorhandler(422)
@app.errorhandler(400)
def handle_error(err):
    headers = err.data.get("headers", None)
    messages = err.data.get("messages", ["Invalid request."])
    if headers:
        return jsonify({"errors": messages}), err.code, headers
    else:
        return jsonify({"errors": messages}), err.code

So that the syntax of VueJS does not collide with that of Jinja2, I deliver the application as a static page from the static folder, bypassing the template engine.

HTML (./static/index.html)
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
</head>
<body>

    <div id="app">
        <form @submit.prevent="onSubmit">
            <div>
                <label for="name">Name</label>
                <input ref="name" v-model="student.name" id="name" autofocus />
                <span v-if="student.name && !isValidName">Name invalid</span>
            </div>
            <div>
                <label for="partner">Partner</label>
                <input ref="partner" v-model="student.partner" id="partner" />
                <span v-if="student.partner && !isValidPartner">Partner invalid</span>
            </div>
            <div>
                <input type="checkbox" v-model="student.can_swim" id="can_swim" />
                <label for="can_swim">Can Swim</label>
            </div>
            <button type="submit" :disabled="!isValid">Add</button>
        </form>

        <div>
            <ul>
                <li  v-for="(student, index) in students" :key="index">
                    <div>
                        {{ student.name }} & 
                        {{ student.partner }}, 
                        {{ student.can_swim ? "Can Swim" : "Can't swim" }}
                    </div>
                </li>               
            </ul>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const app = new Vue({
            el: '#app', 
            data: {
                student: {
                    name: '', 
                    can_swim: false, 
                    partner: ''
                }, 
                students: [], 
            }, 
            methods: {
                onSubmit() {
                    if(!this.isValid) return;

                    const url = '/students/new';
                    fetch(url, {
                        method: 'POST', 
                        headers: {
                            'Content-Type': 'application/json'
                        }, 
                        body: JSON.stringify(this.student)
                    }).then(resp => resp.ok && resp.json())
                        .then(data => {
                            if (data) {
                                this.students.push(data);
                                this.student = {
                                    name: '', 
                                    can_swim: false, 
                                    partner: ''
                                };
                                this.$refs.name.focus();
                            } else {
                                this.$refs.name.select();
                            }
                        });
                }, 
                loadStudents() {
                    const url = '/students'; 
                    return fetch(url)
                        .then(resp => resp.ok && resp.json())
                        .then(data => { return data || []});
                }
            }, 
            computed: {
                isValidName() {
                    return this.student.name 
                            && 2 <= this.student.name.length
                            && this.student.name.length <= 8
                            && this.student.name.match(/^[A-Za-z] [0-9]*$/);
                }, 
                isValidPartner() {
                    return this.student.partner 
                            && 2 <= this.student.partner.length
                            && this.student.partner.length <= 8
                            && this.student.partner.match(/^[A-Za-z] [0-9]*$/);
                }, 
                isValid() {
                    return this.isValidName && this.isValidPartner;
                }
            }, 
            async created() {
                this.students = await this.loadStudents();
            }
        });
    </script>

</body>
</html>

I've tried to guide you through this with comments in the code, and I hope you get on with it.

  • Related