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
Clicking on note allows you to edit the Note model. Let’s add few models!
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:
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!