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.
Files changed (40) hide show
  1. flask_commands-0.1.0/LICENSE +21 -0
  2. flask_commands-0.1.0/PKG-INFO +80 -0
  3. flask_commands-0.1.0/README.md +60 -0
  4. flask_commands-0.1.0/flask_commands/__init__.py +1 -0
  5. flask_commands-0.1.0/flask_commands/cli.py +12 -0
  6. flask_commands-0.1.0/flask_commands/commands/controller.py +0 -0
  7. flask_commands-0.1.0/flask_commands/commands/form.py +0 -0
  8. flask_commands-0.1.0/flask_commands/commands/model.py +0 -0
  9. flask_commands-0.1.0/flask_commands/commands/new.py +60 -0
  10. flask_commands-0.1.0/flask_commands/commands/route.py +0 -0
  11. flask_commands-0.1.0/flask_commands/commands/view.py +171 -0
  12. flask_commands-0.1.0/flask_commands/project/.env +6 -0
  13. flask_commands-0.1.0/flask_commands/project/.env.example +6 -0
  14. flask_commands-0.1.0/flask_commands/project/app/__init__.py +31 -0
  15. flask_commands-0.1.0/flask_commands/project/app/controllers/__init__.py +1 -0
  16. flask_commands-0.1.0/flask_commands/project/app/controllers/main_controller.py +6 -0
  17. flask_commands-0.1.0/flask_commands/project/app/models/__init__.py +1 -0
  18. flask_commands-0.1.0/flask_commands/project/app/models/user.py +51 -0
  19. flask_commands-0.1.0/flask_commands/project/app/routes/mains/__init__.py +5 -0
  20. flask_commands-0.1.0/flask_commands/project/app/routes/mains/routes.py +6 -0
  21. flask_commands-0.1.0/flask_commands/project/app/static/src/input.css +1 -0
  22. flask_commands-0.1.0/flask_commands/project/app/templates/mains/index.html +14 -0
  23. flask_commands-0.1.0/flask_commands/project/config/__init__.py +8 -0
  24. flask_commands-0.1.0/flask_commands/project/config/base_config.py +12 -0
  25. flask_commands-0.1.0/flask_commands/project/config/development_config.py +8 -0
  26. flask_commands-0.1.0/flask_commands/project/config/production_config.py +7 -0
  27. flask_commands-0.1.0/flask_commands/project/run.py +62 -0
  28. flask_commands-0.1.0/flask_commands/project/run.sh +64 -0
  29. flask_commands-0.1.0/flask_commands/utils/__init__.py +1 -0
  30. flask_commands-0.1.0/flask_commands/utils/controllers.py +148 -0
  31. flask_commands-0.1.0/flask_commands/utils/css.py +70 -0
  32. flask_commands-0.1.0/flask_commands/utils/databases.py +32 -0
  33. flask_commands-0.1.0/flask_commands/utils/files.py +88 -0
  34. flask_commands-0.1.0/flask_commands/utils/models.py +119 -0
  35. flask_commands-0.1.0/flask_commands/utils/naming.py +98 -0
  36. flask_commands-0.1.0/flask_commands/utils/routes.py +312 -0
  37. flask_commands-0.1.0/flask_commands/utils/scaffold.py +54 -0
  38. flask_commands-0.1.0/flask_commands/utils/venv.py +48 -0
  39. flask_commands-0.1.0/flask_commands/utils/views.py +65 -0
  40. 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
@@ -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,6 @@
1
+ SECRET_KEY=
2
+ FLASK_APP=
3
+ FLASK_CONFIG=
4
+ APP_NAME=
5
+ SQLALCHEMY_DEVELOPMENT_DATABASE_URI=
6
+ SQLALCHEMY_PRODUCTION_DATABASE_URI=
@@ -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,6 @@
1
+ from flask import render_template
2
+
3
+ class MainController(object):
4
+ @staticmethod
5
+ def index() -> str:
6
+ return render_template('mains/index.html')
@@ -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,5 @@
1
+ from flask import Blueprint
2
+
3
+ bp = Blueprint('mains', __name__)
4
+
5
+ from app.routes.mains import routes
@@ -0,0 +1,6 @@
1
+ from app.controllers import MainController
2
+ from app.routes.mains import bp
3
+
4
+ @bp.route('/', methods=['GET'])
5
+ def index():
6
+ return MainController.index()
@@ -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,8 @@
1
+ from .base_config import BaseConfig
2
+ from .development_config import DevelopmentConfig
3
+ from .production_config import ProductionConfig
4
+
5
+ config = {
6
+ 'development': DevelopmentConfig,
7
+ 'production': ProductionConfig
8
+ }
@@ -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')
@@ -0,0 +1,8 @@
1
+ import os
2
+ from .base_config import BaseConfig
3
+
4
+
5
+ class DevelopmentConfig(BaseConfig):
6
+ # MySQL DataBase Configuration
7
+ SQLALCHEMY_RECORD_QUERIES = True
8
+ SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DEVELOPMENT_DATABASE_URI')
@@ -0,0 +1,7 @@
1
+ import os
2
+ from .base_config import BaseConfig
3
+
4
+
5
+ class ProductionConfig(BaseConfig):
6
+ # MySQL DataBase Configuration
7
+ SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_PRODUCTION_DATABASE_URI')