Build A Note App In Flask The Fastest Possible (Using Shopyo)
Tags: flask, shopyo
Published: 2022-04-13 05:38:16

---
title: Build A Note App In Flask The Fastest Possible (Using Shopyo)
published: true
description: Foundation article for fast Flask apps
tags: flask, shopyo
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2bnjfmc1n1wdqn632hf2.jpeg
canonical_link: https://www.pythonkitchen.com/build-a-note-app-in-flask-as-fast-as-it-can-get-using-shopyo/
---

[Shopyo](https://github.com/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](https://flaskcon.com/y/2021/#), [codebase](https://github.com/flaskcon/traveller)]. This tuto's app is available [here](https://github.com/Abdur-rahmaanJ/shopyo-tuto/tree/stable/note_app)

# First steps

The first step is to install the library.

```bash
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

```bash
mkdir note_app
```

We enter into the folder

```bash
cd note_app
```

Then we create a new Shopyo project

```bash
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

```bash
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

```bash
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

```json
{
    "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 `"/"`

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

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

```python
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`

```python
@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](https://shopyo.readthedocs.io/en/latest/commandline.html#run).

# Creating models

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

```python
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](https://shopyo.readthedocs.io/en/latest/commandline.html#initialise)

```bash
$ 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

```python
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.

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

Then in `app.py` import the Note model

```python
from modules.notes.models import Note
```

And modify the `setup_flask_admin` function to look like this:

```python
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w3ozigkj23q00n5xsjc9.png)

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/re17hbi920kg48gr6lax.png)

# 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

```html
<!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

```python
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptx26y6y8yya7zxqqe5v.png)

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 (comment back the flask-login modifications)

```
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!

This tuto's app is available [here](https://github.com/Abdur-rahmaanJ/shopyo-tuto/tree/stable/note_app)