flask-commands 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flask_commands-0.1.0/LICENSE +21 -0
- flask_commands-0.1.0/PKG-INFO +80 -0
- flask_commands-0.1.0/README.md +60 -0
- flask_commands-0.1.0/flask_commands/__init__.py +1 -0
- flask_commands-0.1.0/flask_commands/cli.py +12 -0
- flask_commands-0.1.0/flask_commands/commands/controller.py +0 -0
- flask_commands-0.1.0/flask_commands/commands/form.py +0 -0
- flask_commands-0.1.0/flask_commands/commands/model.py +0 -0
- flask_commands-0.1.0/flask_commands/commands/new.py +60 -0
- flask_commands-0.1.0/flask_commands/commands/route.py +0 -0
- flask_commands-0.1.0/flask_commands/commands/view.py +171 -0
- flask_commands-0.1.0/flask_commands/project/.env +6 -0
- flask_commands-0.1.0/flask_commands/project/.env.example +6 -0
- flask_commands-0.1.0/flask_commands/project/app/__init__.py +31 -0
- flask_commands-0.1.0/flask_commands/project/app/controllers/__init__.py +1 -0
- flask_commands-0.1.0/flask_commands/project/app/controllers/main_controller.py +6 -0
- flask_commands-0.1.0/flask_commands/project/app/models/__init__.py +1 -0
- flask_commands-0.1.0/flask_commands/project/app/models/user.py +51 -0
- flask_commands-0.1.0/flask_commands/project/app/routes/mains/__init__.py +5 -0
- flask_commands-0.1.0/flask_commands/project/app/routes/mains/routes.py +6 -0
- flask_commands-0.1.0/flask_commands/project/app/static/src/input.css +1 -0
- flask_commands-0.1.0/flask_commands/project/app/templates/mains/index.html +14 -0
- flask_commands-0.1.0/flask_commands/project/config/__init__.py +8 -0
- flask_commands-0.1.0/flask_commands/project/config/base_config.py +12 -0
- flask_commands-0.1.0/flask_commands/project/config/development_config.py +8 -0
- flask_commands-0.1.0/flask_commands/project/config/production_config.py +7 -0
- flask_commands-0.1.0/flask_commands/project/run.py +62 -0
- flask_commands-0.1.0/flask_commands/project/run.sh +64 -0
- flask_commands-0.1.0/flask_commands/utils/__init__.py +1 -0
- flask_commands-0.1.0/flask_commands/utils/controllers.py +148 -0
- flask_commands-0.1.0/flask_commands/utils/css.py +70 -0
- flask_commands-0.1.0/flask_commands/utils/databases.py +32 -0
- flask_commands-0.1.0/flask_commands/utils/files.py +88 -0
- flask_commands-0.1.0/flask_commands/utils/models.py +119 -0
- flask_commands-0.1.0/flask_commands/utils/naming.py +98 -0
- flask_commands-0.1.0/flask_commands/utils/routes.py +312 -0
- flask_commands-0.1.0/flask_commands/utils/scaffold.py +54 -0
- flask_commands-0.1.0/flask_commands/utils/venv.py +48 -0
- flask_commands-0.1.0/flask_commands/utils/views.py +65 -0
- flask_commands-0.1.0/pyproject.toml +37 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Drew Butcher
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flask-commands
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A set of command line tools that help you scaffold out your flask application quickly.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Drew Butcher
|
|
8
|
+
Author-email: drewlbutcher@gmail.com
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Dist: click (>=8.3.1,<9.0.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# flask-commands
|
|
21
|
+
|
|
22
|
+
Local-first CLI that scaffolds a Flask project (venv, dotenv, Tailwind build scripts, blueprints, optional SQLite) and can keep generating views, routes, controllers, and models for you.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
- Python 3.10+
|
|
26
|
+
- Optional: `npm` if you want Tailwind auto-installed; the tool will skip Tailwind if `npm` is missing.
|
|
27
|
+
- `pip install Flask-Commands`
|
|
28
|
+
|
|
29
|
+
> The published console script is currently `flask`. If you have a clash with Flask’s own CLI, run with `python -m flask_commands.cli ...` or rename the script to `flask-commands` in `pyproject.toml`.
|
|
30
|
+
|
|
31
|
+
## Commands at a glance
|
|
32
|
+
- `flask new <project_name> [--db/--no-db]` — bootstrap a new project in a sibling folder, create a venv, install deps, copy the template, wire Tailwind, and optionally initialize SQLite + migrations.
|
|
33
|
+
- `flask make:view <dotted_path_with_name> [options]` — create a view under `app/templates/` and optionally add a controller method, route/blueprint, and model.
|
|
34
|
+
|
|
35
|
+
## Creating a project: `flask new`
|
|
36
|
+
What happens:
|
|
37
|
+
- Creates `<project_name>/` and a virtual environment under `venv/`.
|
|
38
|
+
- Installs Python deps: always `Flask` + `python-dotenv`; adds `Flask-Login`, `Flask-Migrate`, and `Flask-SQLAlchemy` when `--db` (or when you accept the prompt).
|
|
39
|
+
- Freezes `requirements.txt` from the venv.
|
|
40
|
+
- Copies the starter app from `flask_commands/project` (blueprint-based app with configs, sample controller/route, Tailwind input.css, `.env` files, `run.py`, and `run.sh`), and marks `run.sh` executable.
|
|
41
|
+
- Installs Tailwind via npm and adds `watch:css` / `build:css` scripts (skipped with a warning if npm is absent).
|
|
42
|
+
- If `--db` is enabled, runs `flask db init/migrate/upgrade` using the new venv to seed SQLite.
|
|
43
|
+
|
|
44
|
+
Quickstart:
|
|
45
|
+
```bash
|
|
46
|
+
flask new myproject # use --no-db to skip SQLite setup
|
|
47
|
+
cd myproject
|
|
48
|
+
source venv/bin/activate
|
|
49
|
+
flask run --debug # or ./run.sh on macOS to open terminals + Safari and tailwind watchers
|
|
50
|
+
```
|
|
51
|
+
`run.sh` uses `osascript`/`fswatch`, so it is macOS-only.
|
|
52
|
+
|
|
53
|
+
## Generating views/routes/controllers/models: `flask make:view`
|
|
54
|
+
Usage:
|
|
55
|
+
```
|
|
56
|
+
flask make:view <dotted_path_with_name> [--controller NAME|-c] [--route PATH|-r] [--model NAME|-m]
|
|
57
|
+
```
|
|
58
|
+
- `dotted_path_with_name` maps folders + filename under `app/templates/`: `posts.index` → `app/templates/posts/index.html`. Nest as needed: `admin.users.show` → `app/templates/admin/users/show.html`.
|
|
59
|
+
- Without flags you get just the view file (a small HTML snippet with a random Python quote).
|
|
60
|
+
- `-c/--generate-controller` infers a controller class from the path (e.g., `PostController` from `posts.index`) and adds the action method; `--controller` lets you set it explicitly.
|
|
61
|
+
- `-r/--generate-route` infers a RESTful route path (CRUD actions get GET/POST as appropriate, nested resources pick up parent IDs if a matching model import exists in `app/models/__init__.py`); `--route` lets you set a path yourself. If the route folder exists, it appends a function; otherwise it creates a blueprint folder and registers it in `app/__init__.py`.
|
|
62
|
+
- `-m/--generate-model` infers a singular class name and creates a basic SQLAlchemy model file and import stub; `--model` lets you name it explicitly.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
- Minimal view component: `flask make:view button`
|
|
66
|
+
- CRUD start with inferred pieces: `flask make:view posts.index -crm`
|
|
67
|
+
- Nested resource with route params: `flask make:view admin.posts.comments.show -cr`
|
|
68
|
+
- Explicit wiring: `flask make:view posts.show --controller PostController --route /posts/<int:post_id> --model Post`
|
|
69
|
+
|
|
70
|
+
## Project template (copied by `flask new`)
|
|
71
|
+
- Application entrypoints: `run.py`, `run.sh`
|
|
72
|
+
- App package: `app/__init__.py` with blueprint registration + extensions (`LoginManager`, `SQLAlchemy`, `Migrate`)
|
|
73
|
+
- Default blueprint: `app/routes/mains` → `app/controllers/main_controller.py` → `app/templates/mains/index.html`
|
|
74
|
+
- Configs: `config/{base,development,production}_config.py`
|
|
75
|
+
- Static/Tailwind: `app/static/src/input.css` (Tailwind CLI builds `tailwind.css` / `tailwind.min.css`)
|
|
76
|
+
- Environment files: `.env`, `.env.example`
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
PRs and issues welcome. License: MIT.
|
|
80
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# flask-commands
|
|
2
|
+
|
|
3
|
+
Local-first CLI that scaffolds a Flask project (venv, dotenv, Tailwind build scripts, blueprints, optional SQLite) and can keep generating views, routes, controllers, and models for you.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
- Python 3.10+
|
|
7
|
+
- Optional: `npm` if you want Tailwind auto-installed; the tool will skip Tailwind if `npm` is missing.
|
|
8
|
+
- `pip install Flask-Commands`
|
|
9
|
+
|
|
10
|
+
> The published console script is currently `flask`. If you have a clash with Flask’s own CLI, run with `python -m flask_commands.cli ...` or rename the script to `flask-commands` in `pyproject.toml`.
|
|
11
|
+
|
|
12
|
+
## Commands at a glance
|
|
13
|
+
- `flask new <project_name> [--db/--no-db]` — bootstrap a new project in a sibling folder, create a venv, install deps, copy the template, wire Tailwind, and optionally initialize SQLite + migrations.
|
|
14
|
+
- `flask make:view <dotted_path_with_name> [options]` — create a view under `app/templates/` and optionally add a controller method, route/blueprint, and model.
|
|
15
|
+
|
|
16
|
+
## Creating a project: `flask new`
|
|
17
|
+
What happens:
|
|
18
|
+
- Creates `<project_name>/` and a virtual environment under `venv/`.
|
|
19
|
+
- Installs Python deps: always `Flask` + `python-dotenv`; adds `Flask-Login`, `Flask-Migrate`, and `Flask-SQLAlchemy` when `--db` (or when you accept the prompt).
|
|
20
|
+
- Freezes `requirements.txt` from the venv.
|
|
21
|
+
- Copies the starter app from `flask_commands/project` (blueprint-based app with configs, sample controller/route, Tailwind input.css, `.env` files, `run.py`, and `run.sh`), and marks `run.sh` executable.
|
|
22
|
+
- Installs Tailwind via npm and adds `watch:css` / `build:css` scripts (skipped with a warning if npm is absent).
|
|
23
|
+
- If `--db` is enabled, runs `flask db init/migrate/upgrade` using the new venv to seed SQLite.
|
|
24
|
+
|
|
25
|
+
Quickstart:
|
|
26
|
+
```bash
|
|
27
|
+
flask new myproject # use --no-db to skip SQLite setup
|
|
28
|
+
cd myproject
|
|
29
|
+
source venv/bin/activate
|
|
30
|
+
flask run --debug # or ./run.sh on macOS to open terminals + Safari and tailwind watchers
|
|
31
|
+
```
|
|
32
|
+
`run.sh` uses `osascript`/`fswatch`, so it is macOS-only.
|
|
33
|
+
|
|
34
|
+
## Generating views/routes/controllers/models: `flask make:view`
|
|
35
|
+
Usage:
|
|
36
|
+
```
|
|
37
|
+
flask make:view <dotted_path_with_name> [--controller NAME|-c] [--route PATH|-r] [--model NAME|-m]
|
|
38
|
+
```
|
|
39
|
+
- `dotted_path_with_name` maps folders + filename under `app/templates/`: `posts.index` → `app/templates/posts/index.html`. Nest as needed: `admin.users.show` → `app/templates/admin/users/show.html`.
|
|
40
|
+
- Without flags you get just the view file (a small HTML snippet with a random Python quote).
|
|
41
|
+
- `-c/--generate-controller` infers a controller class from the path (e.g., `PostController` from `posts.index`) and adds the action method; `--controller` lets you set it explicitly.
|
|
42
|
+
- `-r/--generate-route` infers a RESTful route path (CRUD actions get GET/POST as appropriate, nested resources pick up parent IDs if a matching model import exists in `app/models/__init__.py`); `--route` lets you set a path yourself. If the route folder exists, it appends a function; otherwise it creates a blueprint folder and registers it in `app/__init__.py`.
|
|
43
|
+
- `-m/--generate-model` infers a singular class name and creates a basic SQLAlchemy model file and import stub; `--model` lets you name it explicitly.
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
- Minimal view component: `flask make:view button`
|
|
47
|
+
- CRUD start with inferred pieces: `flask make:view posts.index -crm`
|
|
48
|
+
- Nested resource with route params: `flask make:view admin.posts.comments.show -cr`
|
|
49
|
+
- Explicit wiring: `flask make:view posts.show --controller PostController --route /posts/<int:post_id> --model Post`
|
|
50
|
+
|
|
51
|
+
## Project template (copied by `flask new`)
|
|
52
|
+
- Application entrypoints: `run.py`, `run.sh`
|
|
53
|
+
- App package: `app/__init__.py` with blueprint registration + extensions (`LoginManager`, `SQLAlchemy`, `Migrate`)
|
|
54
|
+
- Default blueprint: `app/routes/mains` → `app/controllers/main_controller.py` → `app/templates/mains/index.html`
|
|
55
|
+
- Configs: `config/{base,development,production}_config.py`
|
|
56
|
+
- Static/Tailwind: `app/static/src/input.css` (Tailwind CLI builds `tailwind.css` / `tailwind.min.css`)
|
|
57
|
+
- Environment files: `.env`, `.env.example`
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
PRs and issues welcome. License: MIT.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from flask_commands.cli import cli
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from flask_commands.commands.new import new
|
|
3
|
+
from flask_commands.commands.view import make_view
|
|
4
|
+
|
|
5
|
+
@click.group()
|
|
6
|
+
def cli() -> None:
|
|
7
|
+
"""Flask command line tools that will help you build a flask application with blueprints quickly."""
|
|
8
|
+
pass # pragma: no cover
|
|
9
|
+
|
|
10
|
+
# Add commands to the CLI
|
|
11
|
+
cli.add_command(new)
|
|
12
|
+
cli.add_command(make_view)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
import shutil
|
|
4
|
+
from flask_commands.utils.venv import create_venv
|
|
5
|
+
from flask_commands.utils.files import copy_templates
|
|
6
|
+
from flask_commands.utils.css import install_tailwind
|
|
7
|
+
from flask_commands.utils.databases import install_sqlitedb
|
|
8
|
+
|
|
9
|
+
@click.command()
|
|
10
|
+
@click.argument("project_name")
|
|
11
|
+
@click.option("--db/--no-db", default=None, help="Include a database with your project")
|
|
12
|
+
def new(project_name, db):
|
|
13
|
+
"""Create a new Flask project"""
|
|
14
|
+
project_path = os.path.abspath(project_name)
|
|
15
|
+
project_started = False
|
|
16
|
+
if os.path.exists(project_path):
|
|
17
|
+
click.secho(f"💣 Error: Folder Already Exists.", fg="red", bold=True)
|
|
18
|
+
click.secho(f" - Folder '{project_name}' already exists in this directory", fg="red")
|
|
19
|
+
click.secho(f" - Please choose a different project name or change to a new directory", fg="red")
|
|
20
|
+
return
|
|
21
|
+
try:
|
|
22
|
+
project_started = True
|
|
23
|
+
include_db = db if db is not None else click.confirm("Include a Sqlite Database?", default=True)
|
|
24
|
+
os.makedirs(project_path)
|
|
25
|
+
|
|
26
|
+
# Create a Virtual Enviroment and install dependancies and
|
|
27
|
+
# generate a requirments file
|
|
28
|
+
packages = ["Flask", "python-dotenv"]
|
|
29
|
+
if include_db:
|
|
30
|
+
packages.extend(["Flask-Login", "Flask-Migrate", "Flask-SQLAlchemy"])
|
|
31
|
+
create_venv(
|
|
32
|
+
project_path,
|
|
33
|
+
packages=packages,
|
|
34
|
+
freeze_requirements=True)
|
|
35
|
+
|
|
36
|
+
copy_templates(
|
|
37
|
+
project_path,
|
|
38
|
+
include_db=include_db,
|
|
39
|
+
replacements={"project_name": project_name, "project_path": project_path})
|
|
40
|
+
|
|
41
|
+
# Make run.sh executable
|
|
42
|
+
os.chmod(os.path.join(project_path, "run.sh"), 0o755)
|
|
43
|
+
|
|
44
|
+
install_tailwind(project_path)
|
|
45
|
+
|
|
46
|
+
if include_db:
|
|
47
|
+
install_sqlitedb(project_path)
|
|
48
|
+
|
|
49
|
+
click.secho(
|
|
50
|
+
f"{project_name.title()} is ready!!! Run the following:",
|
|
51
|
+
bold=True, underline=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
click.secho(f"cd {project_name}", fg="cyan")
|
|
55
|
+
click.secho("./run.sh", fg="cyan")
|
|
56
|
+
except Exception as exception:
|
|
57
|
+
if project_started and os.path.exists(project_name):
|
|
58
|
+
shutil.rmtree(project_name, ignore_errors=True)
|
|
59
|
+
click.secho("💣 Error: Project Creation Failed 😤", bold=True, fg="red")
|
|
60
|
+
raise click.ClickException(f"exception:\n{exception}") from exception
|
|
File without changes
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
from flask_commands.utils.controllers import (
|
|
5
|
+
controller_add_method,
|
|
6
|
+
controller_infer_name_from,
|
|
7
|
+
controller_make_file
|
|
8
|
+
)
|
|
9
|
+
from flask_commands.utils.models import (
|
|
10
|
+
model_infer_name_from,
|
|
11
|
+
model_make_file
|
|
12
|
+
)
|
|
13
|
+
from flask_commands.utils.naming import camel_to_snake
|
|
14
|
+
from flask_commands.utils.routes import (
|
|
15
|
+
route_add_method,
|
|
16
|
+
route_infer_name_from,
|
|
17
|
+
route_make_directory_and_register_blueprint,
|
|
18
|
+
generate_route_folder_path_and_blueprint_name
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from flask_commands.utils.scaffold import split_dotted_path
|
|
22
|
+
|
|
23
|
+
from flask_commands.utils.views import view_make_file
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@click.command(name="make:view")
|
|
27
|
+
@click.argument("dotted_path_with_name")
|
|
28
|
+
@click.option("--controller", "controller_name", default=None,
|
|
29
|
+
help="Optional controller class name (example PostController).")
|
|
30
|
+
@click.option("-c", "--generate-controller", is_flag=True,
|
|
31
|
+
help="Optional controller flag to generate an inferred controller from the dotted path name.")
|
|
32
|
+
@click.option("--route", "route_name", default=None,
|
|
33
|
+
help="Optional route class name (example /posts).")
|
|
34
|
+
@click.option("-r", "--generate-route", is_flag=True,
|
|
35
|
+
help="Optional route flag to generate an inferred route from the dotted path name.")
|
|
36
|
+
@click.option("--model", "model_name", default=None,
|
|
37
|
+
help="Optional model name (example Post which makes the database table 'posts').")
|
|
38
|
+
@click.option("-m", "--generate-model", is_flag=True,
|
|
39
|
+
help="Optional model flag to generate an inferred model from the dotted path name.")
|
|
40
|
+
def make_view(
|
|
41
|
+
dotted_path_with_name: str,
|
|
42
|
+
controller_name: str | None,
|
|
43
|
+
generate_controller: bool,
|
|
44
|
+
route_name: str | None,
|
|
45
|
+
generate_route: bool,
|
|
46
|
+
model_name: str | None,
|
|
47
|
+
generate_model: bool) -> None:
|
|
48
|
+
"""
|
|
49
|
+
\b
|
|
50
|
+
Create a template view file under app/templates/<folder>/<name>.html.
|
|
51
|
+
You can also optionally connect this view to a controller, route, and model.
|
|
52
|
+
\b
|
|
53
|
+
─── Understanding DOTTED_PATH_WITH_NAME ───
|
|
54
|
+
The dotted path defines the folder and file name:
|
|
55
|
+
<folder>.<name> → app/templates/<folder>/<name>.html
|
|
56
|
+
Example: posts.index → app/templates/posts/index.html
|
|
57
|
+
\b
|
|
58
|
+
You can also nest folders for relationships:
|
|
59
|
+
admin.users.index → app/templates/admin/users/index.html
|
|
60
|
+
posts.images.index → app/templates/posts/images/index.html
|
|
61
|
+
\b
|
|
62
|
+
─── Simple Component Views ───
|
|
63
|
+
For standalone components like a button:
|
|
64
|
+
flask make:view button
|
|
65
|
+
\b
|
|
66
|
+
─── CRUD Views ───
|
|
67
|
+
For RESTful actions (index, show, create, store, edit, update, destroy/delete):
|
|
68
|
+
Initial CRUD setup (controller, route, and model):
|
|
69
|
+
flask make:view posts.index -crm
|
|
70
|
+
flask make:view posts.index --controller PostController --route /posts --model Post
|
|
71
|
+
\b
|
|
72
|
+
Additional CRUD actions (e.g., show):
|
|
73
|
+
flask make:view posts.show -cr
|
|
74
|
+
flask make:view posts.show --controller PostController --route /posts/<int:post_id>
|
|
75
|
+
\b
|
|
76
|
+
─── Flags ───
|
|
77
|
+
Optional flags can be combined as seen above:
|
|
78
|
+
-c / --generate-controller generate inferred controller
|
|
79
|
+
-r / --generate-route generate inferred route
|
|
80
|
+
-m / --generate-model generate inferred model
|
|
81
|
+
\b
|
|
82
|
+
If you prefer explicit control:
|
|
83
|
+
--controller CONTROLLER_NAME set a specific controller
|
|
84
|
+
--route ROUTE_NAME set a specific route
|
|
85
|
+
--model MODEL_NAME set a specific model
|
|
86
|
+
"""
|
|
87
|
+
relative_path, action = split_dotted_path(dotted_path_with_name)
|
|
88
|
+
|
|
89
|
+
# Infer controller name if not provided
|
|
90
|
+
if generate_controller and controller_name is None:
|
|
91
|
+
if relative_path != '':
|
|
92
|
+
controller_name = controller_infer_name_from(relative_path)
|
|
93
|
+
click.secho(f"💡 Info: Inferred controller name as {click.style(controller_name, bold=True)}", fg="cyan")
|
|
94
|
+
else:
|
|
95
|
+
click.secho(f"⚠️ Warning: Could not infer the controller name "
|
|
96
|
+
f"from {dotted_path_with_name}", fg="yellow", bold=True)
|
|
97
|
+
|
|
98
|
+
# Infer route name if not provided
|
|
99
|
+
if generate_route and route_name is None:
|
|
100
|
+
route_name = route_infer_name_from(dotted_path_with_name)
|
|
101
|
+
click.secho("💡 Info: Inferred route name as "
|
|
102
|
+
f"{click.style(route_name, bold=True)}", fg="cyan")
|
|
103
|
+
|
|
104
|
+
# Infer model name if not provided
|
|
105
|
+
if generate_model and model_name is None:
|
|
106
|
+
model_name = model_infer_name_from(relative_path, dotted_path_with_name)
|
|
107
|
+
click.secho(f"💡 Info: Inferred model name as "
|
|
108
|
+
f"{click.style(model_name, bold=True)}", fg="cyan")
|
|
109
|
+
|
|
110
|
+
click.echo("\n")
|
|
111
|
+
|
|
112
|
+
relative_view_file_path = os.path.join(relative_path, f"{action}.html")
|
|
113
|
+
destination_file_path = \
|
|
114
|
+
os.path.join("app", "templates", relative_view_file_path)
|
|
115
|
+
|
|
116
|
+
is_successful, message = view_make_file(destination_file_path)
|
|
117
|
+
click.echo(message)
|
|
118
|
+
|
|
119
|
+
# If a controller_name was provided or inferred
|
|
120
|
+
if controller_name:
|
|
121
|
+
controller_file_path = \
|
|
122
|
+
os.path.join(
|
|
123
|
+
"app",
|
|
124
|
+
"controllers",
|
|
125
|
+
f"{camel_to_snake(controller_name)}.py")
|
|
126
|
+
|
|
127
|
+
# if controller exist just add the method
|
|
128
|
+
if os.path.exists(controller_file_path):
|
|
129
|
+
is_successful, message = controller_add_method(
|
|
130
|
+
controller_name, action, relative_view_file_path, route_name)
|
|
131
|
+
# else create the controller and the method
|
|
132
|
+
else:
|
|
133
|
+
is_successful, message = controller_make_file(
|
|
134
|
+
controller_name, action, relative_view_file_path, route_name)
|
|
135
|
+
click.echo(message)
|
|
136
|
+
|
|
137
|
+
# If a controller_name was provided or inferred
|
|
138
|
+
if route_name:
|
|
139
|
+
route_folder_path, blueprint_name = \
|
|
140
|
+
generate_route_folder_path_and_blueprint_name(
|
|
141
|
+
dotted_path_with_name, relative_path)
|
|
142
|
+
try:
|
|
143
|
+
if os.path.exists(route_folder_path):
|
|
144
|
+
is_successful, message = \
|
|
145
|
+
route_add_method(
|
|
146
|
+
relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
|
|
147
|
+
action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
|
|
148
|
+
route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
|
|
149
|
+
blueprint_name, # posts or mains or posts_comments
|
|
150
|
+
route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
|
|
151
|
+
controller_name) # contoller_name is like post_controller
|
|
152
|
+
else:
|
|
153
|
+
is_successful, message = \
|
|
154
|
+
route_make_directory_and_register_blueprint(
|
|
155
|
+
# relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
|
|
156
|
+
action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
|
|
157
|
+
route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
|
|
158
|
+
blueprint_name, # posts or mains or posts_comments
|
|
159
|
+
route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
|
|
160
|
+
controller_name) # contoller_name is like post_controller
|
|
161
|
+
click.echo(message)
|
|
162
|
+
except Exception as exception:
|
|
163
|
+
click.secho(f"💣 Error:\n {exception}", fg="red")
|
|
164
|
+
|
|
165
|
+
# If a model_name was provided or inferred
|
|
166
|
+
if model_name:
|
|
167
|
+
model_init_path = os.path.join("app", "models", "__init__.py")
|
|
168
|
+
model_file_path = os.path.join("app", "models", f"{model_name.lower()}.py")
|
|
169
|
+
is_successful, message = model_make_file(
|
|
170
|
+
model_name, model_init_path, model_file_path)
|
|
171
|
+
click.echo(message)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
SECRET_KEY=PUT_SOMETHING_SECRET_HERE
|
|
2
|
+
FLASK_APP=run.py
|
|
3
|
+
FLASK_CONFIG=development
|
|
4
|
+
APP_NAME=project_name
|
|
5
|
+
SQLALCHEMY_DEVELOPMENT_DATABASE_URI=sqlite:///project_name_dev.db
|
|
6
|
+
SQLALCHEMY_PRODUCTION_DATABASE_URI=mysql+pymysql://username:password@localhost:3306/project_name_prod
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from config import config
|
|
2
|
+
from flask import Flask
|
|
3
|
+
from flask_login import LoginManager
|
|
4
|
+
from flask_migrate import Migrate
|
|
5
|
+
from flask_sqlalchemy import SQLAlchemy
|
|
6
|
+
|
|
7
|
+
# Logging a user's authentication state handler
|
|
8
|
+
login_manager = LoginManager()
|
|
9
|
+
|
|
10
|
+
# ORM Database handler
|
|
11
|
+
db = SQLAlchemy()
|
|
12
|
+
migrate = Migrate()
|
|
13
|
+
|
|
14
|
+
def create_app(config_name) -> Flask:
|
|
15
|
+
"""Creates a Flask application Instance."""
|
|
16
|
+
app = Flask(__name__)
|
|
17
|
+
|
|
18
|
+
# apply configuration
|
|
19
|
+
app.config.from_object(config[config_name])
|
|
20
|
+
|
|
21
|
+
# initialize extensions: order matters
|
|
22
|
+
login_manager.init_app(app)
|
|
23
|
+
db.init_app(app)
|
|
24
|
+
migrate.init_app(app, db)
|
|
25
|
+
|
|
26
|
+
from app import models
|
|
27
|
+
|
|
28
|
+
from app.routes.mains import bp as mains_blueprint
|
|
29
|
+
app.register_blueprint(mains_blueprint)
|
|
30
|
+
|
|
31
|
+
return app
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main_controller import MainController
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .user import User
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from app import db, login_manager
|
|
2
|
+
from flask_login import UserMixin
|
|
3
|
+
from werkzeug.security import generate_password_hash, check_password_hash
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
|
|
6
|
+
class User(UserMixin, db.Model):
|
|
7
|
+
__tablename__ = 'users'
|
|
8
|
+
# Columns
|
|
9
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
10
|
+
username = db.Column(db.String(64), index=True,
|
|
11
|
+
unique=True, nullable=False)
|
|
12
|
+
password_hash = db.Column(db.String(256), nullable=True)
|
|
13
|
+
created_at = db.Column(db.DateTime(timezone=True),
|
|
14
|
+
index=True,
|
|
15
|
+
default=lambda: datetime.now(timezone.utc))
|
|
16
|
+
updated_at = db.Column(db.DateTime(timezone=True),
|
|
17
|
+
default=lambda: datetime.now(timezone.utc),
|
|
18
|
+
onupdate=lambda: datetime.now(timezone.utc))
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def password(self):
|
|
22
|
+
"""Throw an error when trying to access password attribute"""
|
|
23
|
+
raise AttributeError('password is not a readable attribute')
|
|
24
|
+
|
|
25
|
+
@password.setter
|
|
26
|
+
def password(self, password):
|
|
27
|
+
"""Hash and set password"""
|
|
28
|
+
self.password_hash = generate_password_hash(password)
|
|
29
|
+
|
|
30
|
+
def verify_password(self, password):
|
|
31
|
+
"""Returns true or false by verifing a given password"""
|
|
32
|
+
return check_password_hash(self.password_hash, password)
|
|
33
|
+
|
|
34
|
+
def store_in_database(self):
|
|
35
|
+
db.session.add(self)
|
|
36
|
+
db.session.commit()
|
|
37
|
+
|
|
38
|
+
def delete_from_database(self):
|
|
39
|
+
db.session.delete(self)
|
|
40
|
+
db.session.commit()
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
"""Model representation for code debugging"""
|
|
44
|
+
return f'<User id:{self.id} username:{self.username}>'
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@login_manager.user_loader
|
|
48
|
+
def load_user(user_id):
|
|
49
|
+
"""Load the user from the database, given the id stored
|
|
50
|
+
in the session"""
|
|
51
|
+
return User.query.get(int(user_id))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import 'tailwindcss';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Document</title>
|
|
7
|
+
{%- block styles %}
|
|
8
|
+
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.min.css', v=time.time()) }}">
|
|
9
|
+
{%- endblock styles %}
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
Hello World
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
# Import Environment variables
|
|
6
|
+
|
|
7
|
+
load_dotenv() # reads variables from a .env file and sets them in os.environ
|
|
8
|
+
|
|
9
|
+
class BaseConfig():
|
|
10
|
+
FLASK_CONFIG = os.environ.get('FLASK_CONFIG')
|
|
11
|
+
SECRET_KEY = os.environ.get('SECRET_KEY')
|
|
12
|
+
APP_NAME = os.environ.get('APP_NAME')
|