Flask Guide - Part 5. Restructure Package

Categories:   web development  
Tags:   python   flask  

This is part 5 of the “Flask Guide”.

You can check the previous post here.

We’ll cover more in the next post.

Credits

These are the sources I used:

Restructure Package

This section will cover:

  • Restructure the Package

Organizing our project

  • As the project grows, it’s better to divide the files up into a modular design.
  • We’ll design this project into a package.

The project will end up like this:

.venv
begin/
    __init__.py
    static/main.css
    templates/about.html
    templates/home.html
    templates/layout.html
    templates/login.html
    templates/register.html
    forms.py
    models.py
    routes.py
    site.db
requirements.txt
run.py

What each file looks like

  • run.py:
from begin import app

if __name__ == '__main__':
    app.run(debug=True)

This file will run the application. Notice it’s importing from our package now.


  • begin/__init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SECRET_KEY'] = '67738246378uienohtkxbtx65782'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)

from begin import routes

This file will initialize the application.


  • begin/forms.py:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo


class RegistrationForm(FlaskForm):
    username = StringField('Username',
            validators=[DataRequired(), Length(min=2, max=20)])
    email = StringField('Email',
            validators=[DataRequired(), Email()])
    password = PasswordField('Password',
            validators=[DataRequired(), Length(min=7, max=32)])
    confirm_password = PasswordField('Confirm Password',
            validators=[DataRequired(), Length(min=7, max=32), EqualTo('password')])
    submit = SubmitField('Sign Up')

class LoginForm(FlaskForm):
    email = StringField('Email',
            validators=[DataRequired(), Email()])
    password = PasswordField('Password',
            validators=[DataRequired()])
    remember = BooleanField('Remember Me')
    submit = SubmitField('Login')

We are only importing stuff we need for the forms.


  • models.py:
from begin import db
from datetime import datetime

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
    password = db.Column(db.String(60), nullable=False)
    posts = db.relationship('Post', backref='author', lazy=True)

    def __repr__(self):
        return f"User('{self.username}' ,'{self.email}', '{self.image_file}')"


class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    content = db.Column(db.Text, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

    def __repr__(self):
        return f"Post('{self.title}' ,'{self.date_posted}')"

We are only importing stuff we need for the models.


  • routes.py:
from flask import render_template, url_for, flash, redirect
from begin import app
from begin.forms import RegistrationForm, LoginForm
from begin.models import User, Post

posts = [
    {
        'author': 'John Doe',
        'title': 'Blog Post 1',
        'content': 'First post content',
        'date_posted': '2019, Nov 21'
    },
    {
        'author': 'Jane Doe',
        'title': 'Blog Post 2',
        'content': 'Second post content',
        'date_posted': '2019, Nov 22'
    }
]

@app.route("/")
@app.route("/home")
def home():
    return render_template('home.html', posts=posts)

@app.route("/register", methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        flash(f'Account created for {form.username.data}', 'success')
        return redirect(url_for('home'))
    return render_template('register.html', title='Register', form=form)

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        if form.email.data == 'admin@blog.com' and form.password.data == 'password':
            flash('you have been logged in', 'success')
            return redirect(url_for('home'))
        else:
            flash('Login unsuccessful, please check username and password', 'danger')
    return render_template('login.html', title='Login', form=form)

@app.route("/about")
def about():
    return render_template('about.html', title='About')

Notice we our now importing from our package: begin.forms & begin.models.


The database models

We need to change how we interact with the db now:

python
from begin import db
from begin.models import User, Post

user_1 = User(username='Jim', email='jim@blog.com', password='password')
user_2 = User(username='Jane', email='jane@blog.com', password='password')
db.session.add(user_1)
db.session.add(user_2)
db.session.commit()

User.query.all()
User.query.first()

user = User.query.get(1)

post_1 = Post(title='first post', content='the content', user_id=user.id)
post_2 = Post(title='second post', content='the 2nd content', user_id=user.id)
db.session.add(post_1)
db.session.add(post_2)
db.session.commit()

Post.query.all()

user.posts

db.drop_all()
db.create_all()

We need to use from begin.models import User, Post now.


The templates and static folders are not affected and the site.db file will be created in our package folder.

To run the server, we will use:

py run.py

Summary

  • We changed the file structure of the project.
  • This helps us better organize our code.
  • This also helps prevent circular imports.

I think we will end this section here.

We’ll cover more in the next post.

Related Products



Categories:   web development  
Tags:   python   flask