Build A Note App In Flask As Fast As It Can Get (Using Shopyo)


flask shopyo

Shopyo is a framework to get started with Flask really quick. It includes several libraries to get started by default. In this tutorial we will build a simple app to get started with. We used it for FlaskCon 2021 [site, codebase].

First steps

The first step is to install the library.

python3.9 -m venv venv # create virtual env
. venv/bin/activate # activate (linux version)
pip install shopyo==4.4.3

Then we create our folder

mkdir note_app

We enter into the folder

cd note_app

Then we create a new Shopyo project

shopyo new

The output looks like this:

creating project note_app...
#######################
[x] Project note_app created successfully!

A tree lookup gives

├── docs
│   ├── conf.py
│   ├── docs.rst
│   ├── index.rst
│   ├── Makefile
│   └── _static
│       └── custom.css
├── MANIFEST.in
├── note_app
├── pytest.ini
├── README.md
├── requirements.txt
├── setup.py
└── tox.ini

Since we are not interested in packaging our app for now, we can switch to the inner note_app/ folder

cd note_app

The inner note_app folder has this structure

├── note_app
│   ├── app.py
│   ├── app.txt
│   ├── autoapp.py
│   ├── CHANGELOG.md
│   ├── cli.py
│   ├── config_demo.json
│   ├── config.py
│   ├── conftest.py
│   ├── __init__.py
│   ├── init.py
│   ├── manage.py
│   ├── modules
│   ├── shopyo_admin.py
│   ├── static
│   ├── tests
│   │   ├── conftest.py
│   │   └── test_configs.py
│   └── wsgi.py

Creating modules

Lets create the notes app

shopyo startapp notes

A new folder will be created under modules/. The content of modules/note_app/ looks like this

modules/notes/
├── forms.py
├── global.py
├── info.json
├── models.py
├── static
├── templates
│   └── notes
│       ├── blocks
│       │   └── sidebar.html
│       └── dashboard.html
├── tests
│   ├── test_notes_functional.py
│   └── test_notes_models.py
└── view.py

These are files created by default.

Writing the first view

The info.json file lists some basics about the module, including it’s url namespace (module_name) and url prefix

{
    "author": {
        "mail": "",
        "name": "",
        "website": ""
    },
    "display_string": "Notes",
    "fa-icon": "fa fa-store",
    "module_name": "notes",
    "type": "show",
    "url_prefix": "/notes"
}

Let’s change url_prefix to "/"

{
    ...,
    "url_prefix": "/"
}

Our view.py looks like this, which is generated by default.

from shopyo.api.module import ModuleHelp

mhelp = ModuleHelp(__file__, __name__)
globals()[mhelp.blueprint_str] = mhelp.blueprint
module_blueprint = globals()[mhelp.blueprint_str]


@module_blueprint.route("/")
def index():
    return mhelp.info['display_string']

Let’s change the return string to String from notes app

@module_blueprint.route("/")
def index():
    return "String from notes app"

Running the app

Run shopyo rundebug to run the app in debug mode.

Going to “http://127.0.0.1:5000/” should return “String from notes app”

You can learn more about the run command here.

Creating models

Our notes will have a title and a content. In modules/notes/models.py write:

from init import db
from shopyo.api.models import PkModel



class Note(PkModel): 

    __tablename__ = 'notes'

    title = db.Column(db.String(80), nullable=False)
    content = db.Text()

The init import comes from the init.py file. The PkModel is same as db.Model with by default id as primary key

Then we initialise the app. It uses Flask-Migrate under the hood. You can view more initialise options here

$ shopyo initialise
initializing...
Cleaning...
#######################
Auto importing models...
#######################

Creating db...
#######################

Migrating db...
#######################

Upgrading db...
#######################

Collecting static...
#######################

Uploading initial data to db...
#######################

All Done!

This worked as Shopyo adds a shopyo.db as basic sqlalchemy connection string.

Configuring Flask-Admin

Now let’s configure flask-admin to have a quick CRUD view. Fortunately, Shopyo already has some basics ongoing.

First, modify shopyo_admin.py to remove Flask-Login authentications

from flask import redirect
from flask import request
from flask import url_for
from flask_admin import AdminIndexView
from flask_admin import expose
from flask_admin.contrib import sqla as flask_admin_sqla
from flask_login import current_user


class DefaultModelView(flask_admin_sqla.ModelView):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    # def is_accessible(self):
    #     return current_user.is_authenticated and current_user.is_admin

    # def inaccessible_callback(self, name, **kwargs):
    #     # redirect to login page if user doesn't have access
    #     return redirect(url_for("auth.login", next=request.url))


class MyAdminIndexView(AdminIndexView):
    # def is_accessible(self):
    #     return current_user.is_authenticated and current_user.is_admin

    # def inaccessible_callback(self, name, **kwargs):
    #     # redirect to login page if user doesn't have access
    #     return redirect(url_for("auth.login", next=request.url))

    @expose("/")
    def index(self):
        # if not current_user.is_authenticated and current_user.is_admin:
        #     return redirect(url_for("auth.login"))
        return super().index()
# 
    @expose("/dashboard")
    def indexs(self):
        # if not current_user.is_authenticated and current_user.is_admin:
        #     return redirect(url_for("auth.login"))
        return super().index()

Then in app.py, don’t load Flask-Login by commenting it out.

def load_extensions(app):
    ...
    # login_manager.init_app(app)

Then in app.py import the Note model

from modules.notes.models import Note

And modify the setup_flask_admin function to look like this:

def setup_flask_admin(app):
    admin = Admin(
        app,
        name="My App",
        template_mode="bootstrap4",
        index_view=MyAdminIndexView(),
    )
    admin.add_view(ModelView(Note, db.session))

Now, navigating to /admin gives

Image description

Clicking on note allows you to edit the Note model. Let’s add few models!

Image description Displaying templates ====================

What remains is using displaying the note on our main page.

Under modules/notes/templates/notes create a file called index.html with content

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
</head>
<body>
    {% for note in notes %}
        {{note.title}}<br>
        {{note.content}}<hr>
    {% endfor %}
</body>
</html>

Modify the view.py to

from shopyo.api.module import ModuleHelp
from flask import render_template

from .models import Note 


mhelp = ModuleHelp(__file__, __name__)
globals()[mhelp.blueprint_str] = mhelp.blueprint
module_blueprint = globals()[mhelp.blueprint_str]


@module_blueprint.route("/")
def index():
    notes = Note.query.all()
    return render_template('notes/index.html', notes=notes)

Which results in:

Image description

Shopyo also provides some utils like

from shopyo.api.templates import yo_render

...
@module_blueprint.route("/")
def index():
    notes = Note.query.all()
    context = {
        'notes': notes
    }
    return yo_render('notes/index.html', context)

Trying out a demo app

If you just want to try a demo app, just run

mkdir project
cd project
shopyo new -m # -m adds default modules
cd project
shopyo initialise
shopyo rundebug

You can then see how an authed Flask-Admin looks like.

Hope you enjoy this post!