pythonflaskforeign-keysflask-sqlalchemytable-relationships

Writing into three databases with one submit with flask



I started playing around with flask a week ago and facing already a little problem and I don't know if flask could handle it as a framework or if I need to solve it the "old" way.
Here is the situation.

I have three dbs Platten, Label and Artist. To fill the tables I have one form where I can input the Infos for the LP (Artist, Label, Release date, etc.).
By submitting the form I want to check the Artist db, if the Artist is already existend or not. If exitend, take the id and save it for the LP. If the artist is not in the db, write it and save the new id.
Same for the Label.

This is my models.py file: from app import db from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin from app import login_manager

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)


class Label(db.Model):
    __tablename__ = 'labels'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return '<Label {}>'.format(self.name)


class Artist(db.Model):
    __tablename__ = 'artists'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return '<Artist {}>'.format(self.name)


class Platte(db.Model):
    __tablename__ = 'platten'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(64), index=True)
    label_id = db.Column(db.Integer, db.ForeignKey('labels.id'))
    label = db.relationship('Label', backref=db.backref('platten'), lazy=True)
    release_date = db.Column(db.Date())
    artist_id = db.Column(db.Integer, db.ForeignKey('artists.id'))
    artist = db.relationship('Artist', backref=db.backref('platten'), lazy=True)

    def __repr__(self):
        return '<Platte {}>'.format(self.title)

This is my routes.py file:

from flask import render_template, flash, redirect, url_for, request
from app import app, db
from app.forms import LoginForm, RegistrationForm, PlattenForm
from flask_login import current_user, login_user, logout_user, login_required
from app.models import User, Platte, Artist, Label
from werkzeug.urls import url_parse

@app.route('/')
@app.route('/index')
@login_required
def index():
    platten = Platte.query.all()

    return render_template('index.html', title='Home', platten=platten)

@app.route('/add', methods=['GET', 'POST'])
@login_required
def platten_add():
    form = PlattenForm()
    if form.validate_on_submit():
        artist = Artist(name=form.artist.data)
        label = Label(name=form.label.data)

        # artist_check = Artist.query.filter_by(name=artist).first()
        # if artist_check is None:
        #     db.session.add(artist)
        #     db.session.commit()
        #     artist_id = Artist.query.filter_by(name=artist).first()
        #     artist_id = artist_id.id
        # else:
        #     artist_id = artist_check.id
        platte = Platte(title=form.title.data, label_id=label.id, label=label, artist_id=artist.id, artist=artist, release_date=form.release_date.data)
        db.session.add(platte)
        db.session.commit()
        flash('Platte ' + platte.title + ', hinzugefügt!')
        return redirect(url_for('platten_add'))
    return render_template('platten_add.html', title='Add', form=form)

The commented part in the routes.py would be the "old" fashioned way, how I would do the db insert.

Is there a way to do this within flask or do I have to do it manually like in the commented section of the code? Thanks for the help.


Solution

  • No, that is not possible to do in flask_sqlalchemy. In order to access an objects id like artist.id it must already be saved.

    Also for it to be one-to many, this is the preferred way to do it:

    class Label(db.Model):
        __tablename__ = 'labels'
    
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True)
        platten = db.relationship('Platte', backref='labels')
    
        def __repr__(self):
            return '<Label {}>'.format(self.name)
    
    
    class Artist(db.Model):
        __tablename__ = 'artists'
    
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True)
        platten = db.relationship('Platte', backref='artists')
    
        def __repr__(self):
            return '<Artist {}>'.format(self.name)
    
    
    class Platte(db.Model):
        __tablename__ = 'platten'
    
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(64), index=True)
        label_id = db.Column(db.Integer, db.ForeignKey('labels.id'))
        release_date = db.Column(db.Date())
        artist_id = db.Column(db.Integer, db.ForeignKey('artists.id'))
    
        def __repr__(self):
            return '<Platte {}>'.format(self.title)
    

    Which in turn means you now can do:

    @app.route('/add', methods=['GET', 'POST'])
    @login_required
    def platten_add():
        form = PlattenForm()
        if form.validate_on_submit():
    
            artist = Artist.query.filter_by(name=form.artist.data).first()
            if not artist:
                artist = Artist(name=form.artist.data)
                db.session.add(artist)
            label = Label.query.filter_by(name=form.label.data).first()
            if not label:
                label = Label(name=form.label.data)
                db.session.add(label)
    
            platte = Platte(title=form.title.data, release_date=form.release_date.data)
    
            label.platten.append(platte)
            artist.platten.append(platte)
    
            db.session.commit()
            flash('Platte ' + platte.title + ', hinzugefügt!')
            return redirect(url_for('platten_add'))
        return render_template('platten_add.html', title='Add', form=form)