DEV Community

Cover image for Build A Note App In Flask The Fastest Possible (Using Shopyo)
Abdur-Rahmaan Janhangeer
Abdur-Rahmaan Janhangeer

Posted on

Build A Note App In Flask The Fastest Possible (Using 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]. This tuto's app is available here

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
Enter fullscreen mode Exit fullscreen mode

Then we create our folder

mkdir note_app
Enter fullscreen mode Exit fullscreen mode

We enter into the folder

cd note_app
Enter fullscreen mode Exit fullscreen mode

Then we create a new Shopyo project

shopyo new
Enter fullscreen mode Exit fullscreen mode

The output looks like this:

creating project note_app...
#######################
[x] Project note_app created successfully!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

cd note_app
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Creating modules

Lets create the notes app

shopyo startapp notes
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

Let's change url_prefix to "/"

{
    ...,
    "url_prefix": "/"
}
Enter fullscreen mode Exit fullscreen mode

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']
Enter fullscreen mode Exit fullscreen mode

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

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

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()
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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

def load_extensions(app):
    ...
    # login_manager.init_app(app)
Enter fullscreen mode Exit fullscreen mode

Then in app.py import the Note model

from modules.notes.models import Note
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Trying out a demo app

If you just want to try a demo app, just run (comment back the flask-login modifications)

mkdir project
cd project
shopyo new -m # -m adds default modules
cd project
shopyo initialise
shopyo rundebug
Enter fullscreen mode Exit fullscreen mode

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

Hope you enjoy this post!

This tuto's app is available here

Top comments (0)