flaskteroids 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.
- flaskteroids-0.1.0/LICENSE +21 -0
- flaskteroids-0.1.0/PKG-INFO +564 -0
- flaskteroids-0.1.0/README.md +524 -0
- flaskteroids-0.1.0/flaskteroids/__init__.py +31 -0
- flaskteroids-0.1.0/flaskteroids/__main__.py +5 -0
- flaskteroids-0.1.0/flaskteroids/actions.py +207 -0
- flaskteroids-0.1.0/flaskteroids/app.py +158 -0
- flaskteroids-0.1.0/flaskteroids/cache/__init__.py +27 -0
- flaskteroids-0.1.0/flaskteroids/cache/base.py +1 -0
- flaskteroids-0.1.0/flaskteroids/cache/factory.py +5 -0
- flaskteroids-0.1.0/flaskteroids/cache/inmemory.py +30 -0
- flaskteroids-0.1.0/flaskteroids/cli/artifacts.py +55 -0
- flaskteroids-0.1.0/flaskteroids/cli/db/commands.py +32 -0
- flaskteroids-0.1.0/flaskteroids/cli/db/config.py +21 -0
- flaskteroids-0.1.0/flaskteroids/cli/db/templates/default/env.py +81 -0
- flaskteroids-0.1.0/flaskteroids/cli/flaskteroids.py +119 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/authentication/generator.py +38 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/cmd_parser.py +52 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/commands.py +71 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/controller/generator.py +54 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/fields.py +7 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/mailer/cmd_parser.py +25 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/mailer/generator.py +50 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/migrations/cmd_parser.py +198 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/migrations/generator.py +25 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/model/generator.py +20 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/resource/cmd_parser.py +38 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/resource/generator.py +24 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/scaffold/cmd_parser.py +43 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/scaffold/generator.py +32 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/src_modifier.py +58 -0
- flaskteroids-0.1.0/flaskteroids/cli/generators/templates.py +10 -0
- flaskteroids-0.1.0/flaskteroids/concern.py +4 -0
- flaskteroids-0.1.0/flaskteroids/controller.py +86 -0
- flaskteroids-0.1.0/flaskteroids/controllers/health_controller.py +6 -0
- flaskteroids-0.1.0/flaskteroids/controllers/welcome_controller.py +59 -0
- flaskteroids-0.1.0/flaskteroids/csrf.py +28 -0
- flaskteroids-0.1.0/flaskteroids/current.py +24 -0
- flaskteroids-0.1.0/flaskteroids/db.py +17 -0
- flaskteroids-0.1.0/flaskteroids/discovery.py +33 -0
- flaskteroids-0.1.0/flaskteroids/exceptions.py +18 -0
- flaskteroids-0.1.0/flaskteroids/extensions/db.py +81 -0
- flaskteroids-0.1.0/flaskteroids/extensions/jobs.py +61 -0
- flaskteroids-0.1.0/flaskteroids/extensions/mail.py +20 -0
- flaskteroids-0.1.0/flaskteroids/extensions/routes.py +230 -0
- flaskteroids-0.1.0/flaskteroids/extensions/utils.py +33 -0
- flaskteroids-0.1.0/flaskteroids/fields.py +124 -0
- flaskteroids-0.1.0/flaskteroids/flash.py +20 -0
- flaskteroids-0.1.0/flaskteroids/form.py +119 -0
- flaskteroids-0.1.0/flaskteroids/helpers/__init__.py +55 -0
- flaskteroids-0.1.0/flaskteroids/inflector.py +136 -0
- flaskteroids-0.1.0/flaskteroids/jobs/job.py +13 -0
- flaskteroids-0.1.0/flaskteroids/mailer.py +132 -0
- flaskteroids-0.1.0/flaskteroids/model.py +543 -0
- flaskteroids-0.1.0/flaskteroids/rate_limit.py +26 -0
- flaskteroids-0.1.0/flaskteroids/registry.py +13 -0
- flaskteroids-0.1.0/flaskteroids/rules.py +53 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/PKG-INFO +564 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/SOURCES.txt +66 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/dependency_links.txt +1 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/entry_points.txt +2 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/requires.txt +5 -0
- flaskteroids-0.1.0/flaskteroids.egg-info/top_level.txt +1 -0
- flaskteroids-0.1.0/pyproject.toml +41 -0
- flaskteroids-0.1.0/setup.cfg +8 -0
- flaskteroids-0.1.0/tests/test_app.py +18 -0
- flaskteroids-0.1.0/tests/test_inflector.py +99 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Juan Sebastian Quintero
|
|
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,564 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flaskteroids
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Flask on steroids
|
|
5
|
+
Author: Juan Sebastian Quintero
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Juan Sebastian Quintero
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Classifier: Programming Language :: Python :: 3
|
|
29
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
30
|
+
Classifier: Operating System :: OS Independent
|
|
31
|
+
Classifier: Framework :: Flask
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Requires-Dist: alembic>=1.15.1
|
|
35
|
+
Requires-Dist: Flask
|
|
36
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
37
|
+
Requires-Dist: sqlalchemy
|
|
38
|
+
Requires-Dist: celery
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<a href="https://github.com/nullco/flaskteroids">
|
|
43
|
+
<img src="https://raw.githubusercontent.com/nullco/flaskteroids/main/docs/images/logo.png" alt="Flaskteroids Logo" width="200">
|
|
44
|
+
</a>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<h1 align="center">Flaskteroids</h1>
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<strong>Flaskteroids: A complete, batteries-included Python MVC framework inspired by Ruby on Rails.</strong>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<a href="https://github.com/nullco/flaskteroids/actions/workflows/test.yml?query=branch%3Amain++" target="_blank">
|
|
55
|
+
<img src="https://github.com/nullco/flaskteroids/actions/workflows/test.yml/badge.svg?event=push&branch=main" alt="Build Status">
|
|
56
|
+
</a>
|
|
57
|
+
<a href="https://pypi.org/project/flaskteroids/" target="_blank">
|
|
58
|
+
<img src="https://img.shields.io/pypi/v/flaskteroids.svg" alt="PyPI Version">
|
|
59
|
+
</a>
|
|
60
|
+
<a href="https://pypi.org/project/flaskteroids/" target="_blank">
|
|
61
|
+
<img src="https://img.shields.io/pypi/pyversions/flaskteroids.svg" alt="Python Versions">
|
|
62
|
+
</a>
|
|
63
|
+
<a href="https://github.com/nullco/flaskteroids/blob/main/LICENSE" target="_blank">
|
|
64
|
+
<img src="https://img.shields.io/github/license/nullco/flaskteroids.svg" alt="License">
|
|
65
|
+
</a>
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
**Flaskteroids** (or Flask on Steroids) is a lightweight yet powerful Python MVC framework that supercharges **Flask** with a clean, elegant structure. Inspired by the best of **Ruby on Rails**, it brings clarity and productivity to your web development workflow.
|
|
72
|
+
|
|
73
|
+
Built on the philosophy of **convention over configuration**, Flaskteroids helps you move fast, write less code, and stay focused on what matters: building scalable, maintainable applications with confidence.
|
|
74
|
+
|
|
75
|
+
With a **batteries-included** approach and carefully chosen core dependencies, Flaskteroids stays lean, fast, and secure — giving you everything you need, and nothing you don’t.
|
|
76
|
+
|
|
77
|
+
## Features
|
|
78
|
+
|
|
79
|
+
- **Full MVC Architecture**: Clean separation of concerns with Models, Views, and Controllers.
|
|
80
|
+
- **Elegant Routing**: Intuitive and resourceful routing.
|
|
81
|
+
- **Built-in ORM**: Seamless integration with SQLAlchemy and Alembic for database management and migrations.
|
|
82
|
+
- **Background Jobs**: Integrated with Celery for easy background job processing.
|
|
83
|
+
- **RESTful by Design**: Quickly build API routes with JSON responses.
|
|
84
|
+
- **Powerful CLI**: A rich set of commands for generating models, controllers, mailers, scaffolds, and more.
|
|
85
|
+
- **Flask Compatibility**: Retains the full power of Flask and its rich ecosystem.
|
|
86
|
+
|
|
87
|
+
## Table of Contents
|
|
88
|
+
|
|
89
|
+
- [Installation](#installation)
|
|
90
|
+
- [Getting Started](#getting-started)
|
|
91
|
+
- [Creating a New App](#1-creating-a-new-app)
|
|
92
|
+
- [Running the Server](#2-running-the-server)
|
|
93
|
+
- [Your First Feature: A Blog](#your-first-feature-a-blog)
|
|
94
|
+
- [Core Concepts](#core-concepts)
|
|
95
|
+
- [Project Structure](#project-structure)
|
|
96
|
+
- [Routing](#routing)
|
|
97
|
+
- [Controllers](#controllers)
|
|
98
|
+
- [Controller Callbacks](#controller-callbacks)
|
|
99
|
+
- [Content Negotiation](#content-negotiation)
|
|
100
|
+
- [Models](#models)
|
|
101
|
+
- [Views](#views)
|
|
102
|
+
- [Mailers](#mailers)
|
|
103
|
+
- [Background Jobs](#background-jobs)
|
|
104
|
+
- [Flash Messaging](#flash-messaging)
|
|
105
|
+
- [Handling Forms](#handling-forms)
|
|
106
|
+
- [Building Forms](#building-forms)
|
|
107
|
+
- [Strong Parameters](#strong-parameters)
|
|
108
|
+
- [CSRF Protection](#csrf-protection)
|
|
109
|
+
- [Security](#security)
|
|
110
|
+
- [Rate Limiting](#rate-limiting)
|
|
111
|
+
- [Command-Line Interface (CLI)](#command-line-interface-cli)
|
|
112
|
+
- [Generators](#generators)
|
|
113
|
+
- [Testing](#testing)
|
|
114
|
+
- [Contributing](#contributing)
|
|
115
|
+
- [License](#license)
|
|
116
|
+
|
|
117
|
+
## Installation
|
|
118
|
+
|
|
119
|
+
To get started, install the Flaskteroids package using `pip`:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
pip install flaskteroids
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Getting Started
|
|
126
|
+
|
|
127
|
+
### 1. Creating a New App
|
|
128
|
+
|
|
129
|
+
Create a new Flaskteroids application using the `flaskteroids new` command:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
flaskteroids new my_project
|
|
133
|
+
cd my_project
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This creates a new directory called `my_project` with a standard application structure.
|
|
137
|
+
|
|
138
|
+
### 2. Running the Server
|
|
139
|
+
|
|
140
|
+
To start the development server, run:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
flask run
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Now, open your browser and navigate to `http://127.0.0.1:5000`. You should see the Flaskteroids welcome page!
|
|
147
|
+
|
|
148
|
+
## Your First Feature: A Blog
|
|
149
|
+
|
|
150
|
+
Let's create a simple blog to see the power of scaffolding.
|
|
151
|
+
|
|
152
|
+
1. **Create a new app:**
|
|
153
|
+
This command will create a new Flaskteroids application in a directory called `my_blog`.
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
flaskteroids new my_blog
|
|
157
|
+
cd my_blog
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
2. **Generate a Post scaffold:**
|
|
161
|
+
This command will create the model, controller, views, and database migration for a `Post` resource with `title` and `content` fields.
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
flask generate scaffold Post title:string content:text
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
3. **Run the database migration:**
|
|
168
|
+
Apply the changes to your database schema.
|
|
169
|
+
|
|
170
|
+
```sh
|
|
171
|
+
flask db:migrate
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
4. **Start the server:**
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
flask run
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Now, visit `http://1227.0.0.1:5000/posts` in your browser. You have a complete set of pages to create, view, update, and delete posts.
|
|
181
|
+
|
|
182
|
+
## Core Concepts
|
|
183
|
+
|
|
184
|
+
### Project Structure
|
|
185
|
+
|
|
186
|
+
A typical Flaskteroids project follows this structure:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
my_project/
|
|
190
|
+
├── app/
|
|
191
|
+
│ ├── controllers/
|
|
192
|
+
│ ├── models/
|
|
193
|
+
│ ├── mailers/
|
|
194
|
+
│ ├── views/
|
|
195
|
+
│ └── jobs/
|
|
196
|
+
├── config/
|
|
197
|
+
│ └── routes.py
|
|
198
|
+
└── run.py
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- `app/controllers`: Handle web requests and respond with data or rendered views.
|
|
202
|
+
- `app/models`: Define your application's data structure and database interactions.
|
|
203
|
+
- `app/views`: Contain the templates for your application's UI.
|
|
204
|
+
- `app/mailers`: Handle sending emails.
|
|
205
|
+
- `app/jobs`: Define background jobs to be run asynchronously.
|
|
206
|
+
- `config/routes.py`: Define the URL routes for your application.
|
|
207
|
+
|
|
208
|
+
### Routing
|
|
209
|
+
|
|
210
|
+
Routes are defined in `config/routes.py` by a `register` function that receives a router instance. You can define standard routes or use resourceful routing to handle RESTful conventions automatically.
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
# config/routes.py
|
|
214
|
+
def register(router):
|
|
215
|
+
# A standard route
|
|
216
|
+
router.get("/", to="welcome#index")
|
|
217
|
+
|
|
218
|
+
# Resourceful routes for a 'posts' controller
|
|
219
|
+
router.resources("posts")
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Controllers
|
|
223
|
+
|
|
224
|
+
Controllers handle the logic for incoming requests. Actions are methods within a controller class that set instance variables for the view. Rendering is handled automatically.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
# app/controllers/posts_controller.py
|
|
228
|
+
from flaskteroids import params
|
|
229
|
+
from flaskteroids.controller import ActionController
|
|
230
|
+
from app.controllers.application_controller import ApplicationController
|
|
231
|
+
from app.models.post import Post
|
|
232
|
+
|
|
233
|
+
class PostsController(ApplicationController):
|
|
234
|
+
def index(self):
|
|
235
|
+
self.posts = Post.all()
|
|
236
|
+
# Implicitly renders app/views/posts/index.html and
|
|
237
|
+
# makes @posts available in the template.
|
|
238
|
+
|
|
239
|
+
def show(self):
|
|
240
|
+
self.post = Post.find(params["id"])
|
|
241
|
+
# Implicitly renders app/views/posts/show.html and
|
|
242
|
+
# makes @post available in the template.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Controller Callbacks
|
|
246
|
+
|
|
247
|
+
You can use callbacks to run code before a controller action. The `before_action` callback is useful for setting up instance variables or performing authentication checks. Callbacks must be defined within a `@rules` decorator.
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
from flaskteroids import params
|
|
251
|
+
from flaskteroids.actions import before_action
|
|
252
|
+
from flaskteroids.rules import rules
|
|
253
|
+
from app.controllers.application_controller import ApplicationController
|
|
254
|
+
from app.models.post import Post
|
|
255
|
+
|
|
256
|
+
@rules(
|
|
257
|
+
before_action('_set_post', only=['show', 'edit', 'update', 'destroy'])
|
|
258
|
+
)
|
|
259
|
+
class PostsController(ActionController):
|
|
260
|
+
def show(self):
|
|
261
|
+
# self.post is already set by the callback
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
def _set_post(self):
|
|
265
|
+
self.post = Post.find(params["id"])
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Content Negotiation
|
|
269
|
+
|
|
270
|
+
Flaskteroids can handle different response formats within a single action using the `respond_to` block. This is particularly useful for building APIs that serve both HTML and JSON.
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from flaskteroids.controller import respond_to
|
|
274
|
+
from app.controllers.application_controller import ApplicationController
|
|
275
|
+
from app.models.post import Post
|
|
276
|
+
|
|
277
|
+
class PostsController(ActionController):
|
|
278
|
+
def index(self):
|
|
279
|
+
self.posts = Post.all()
|
|
280
|
+
with respond_to() as format:
|
|
281
|
+
format.html(lambda: render('index'))
|
|
282
|
+
format.json(lambda: render(json=self.posts))
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Models
|
|
286
|
+
|
|
287
|
+
Models inherit from `flaskteroids.model.Model` and act as a rich wrapper around your database tables. Database columns are defined during migration and are automatically available as attributes on the model.
|
|
288
|
+
|
|
289
|
+
The `Model` class provides a powerful API for associations and data validation.
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
# app/models/post.py
|
|
293
|
+
from flaskteroids.model import Model, validates, belongs_to, has_many
|
|
294
|
+
from flaskteroids.rules import rules
|
|
295
|
+
|
|
296
|
+
@rules(
|
|
297
|
+
# --- Associations ---
|
|
298
|
+
belongs_to('user'),
|
|
299
|
+
has_many('comments'),
|
|
300
|
+
|
|
301
|
+
# --- Validations ---
|
|
302
|
+
validates('title', presence=True, length={'minimum': 5}),
|
|
303
|
+
validates('content', presence=True)
|
|
304
|
+
)
|
|
305
|
+
class Post(Model):
|
|
306
|
+
# The database columns (e.g., title, content, user_id) are defined
|
|
307
|
+
# in the database migration and automatically mapped to this model.
|
|
308
|
+
pass
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Views
|
|
312
|
+
|
|
313
|
+
Views are the user-facing part of your application. Flaskteroids uses Jinja2 for templating, which is the default for Flask. Templates are located in the `app/views` directory and are automatically rendered by controller actions.
|
|
314
|
+
|
|
315
|
+
Instance variables set in the controller are available in the corresponding view file.
|
|
316
|
+
|
|
317
|
+
```html
|
|
318
|
+
<!-- app/views/posts/index.html -->
|
|
319
|
+
{% extends "layouts/application.html" %}
|
|
320
|
+
|
|
321
|
+
{% block body %}
|
|
322
|
+
<h1>Posts</h1>
|
|
323
|
+
|
|
324
|
+
<ul>
|
|
325
|
+
{% for post in posts %}
|
|
326
|
+
<li>{{ post.title }}</li>
|
|
327
|
+
{% endfor %}
|
|
328
|
+
</ul>
|
|
329
|
+
{% endblock %}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Mailers
|
|
333
|
+
|
|
334
|
+
Mailers inherit from `ActionMailer` and are used to send emails from your application. They work similarly to controllers, with actions that correspond to email templates.
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
# app/mailers/user_mailer.py
|
|
338
|
+
from flaskteroids.mailer import ActionMailer
|
|
339
|
+
|
|
340
|
+
class UserMailer(ActionMailer):
|
|
341
|
+
def welcome_email(self, user):
|
|
342
|
+
self.user = user
|
|
343
|
+
self.mail(to=user.email, subject="Welcome to My App!")
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
You can then deliver the email synchronously or asynchronously:
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
# Deliver now
|
|
350
|
+
UserMailer().welcome_email(user).deliver_now()
|
|
351
|
+
|
|
352
|
+
# Deliver later using a background job
|
|
353
|
+
UserMailer().welcome_email(user).deliver_later()
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Background Jobs
|
|
357
|
+
|
|
358
|
+
For long-running tasks, you can use background jobs. Jobs inherit from `Job` and define a `perform` method.
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
# app/jobs/my_job.py
|
|
362
|
+
from flaskteroids.jobs.job import Job
|
|
363
|
+
|
|
364
|
+
class MyJob(Job):
|
|
365
|
+
def perform(self, user_id):
|
|
366
|
+
# Do some long-running task
|
|
367
|
+
print(f"Performing job for user {user_id}")
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
To enqueue a job, call `perform_later`:
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
MyJob().perform_later(user_id=1)
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Flash Messaging
|
|
377
|
+
|
|
378
|
+
Flaskteroids provides a `flash` object for setting and displaying temporary messages.
|
|
379
|
+
|
|
380
|
+
In your controller, you can set a message like this:
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
from flaskteroids.flash import flash
|
|
384
|
+
|
|
385
|
+
class PostsController(ActionController):
|
|
386
|
+
def create(self):
|
|
387
|
+
# ... create post
|
|
388
|
+
flash['notice'] = "Post was successfully created."
|
|
389
|
+
# redirect
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Then, in your view, you can display the message:
|
|
393
|
+
|
|
394
|
+
```html
|
|
395
|
+
{% if flash.notice %}
|
|
396
|
+
<div class="notice">{{ flash.notice }}</div>
|
|
397
|
+
{% endif %}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Handling Forms
|
|
401
|
+
|
|
402
|
+
Handling user input from forms is a common task in web applications. Flaskteroids provides a cohesive set of tools to build forms, handle submissions securely, and prevent common vulnerabilities.
|
|
403
|
+
|
|
404
|
+
#### Building Forms
|
|
405
|
+
|
|
406
|
+
Flaskteroids provides a simple and powerful way to build forms using the `form_with` helper and the `Form` object. The `form_with` helper can be used with a model object to automatically generate the form's action and method, and it also includes the CSRF token automatically.
|
|
407
|
+
|
|
408
|
+
Here's an example of how to create a form for a `Post` model:
|
|
409
|
+
|
|
410
|
+
```html
|
|
411
|
+
<!-- app/views/posts/_form.html -->
|
|
412
|
+
{% call(form) form_with(model=post) %}
|
|
413
|
+
<div>
|
|
414
|
+
{{ form.label('title') }}
|
|
415
|
+
{{ form.text_field('title') }}
|
|
416
|
+
</div>
|
|
417
|
+
|
|
418
|
+
<div>
|
|
419
|
+
{{ form.label('content') }}
|
|
420
|
+
{{ form.text_area('content') }}
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
<div>
|
|
424
|
+
{{ form.submit('Save') }}
|
|
425
|
+
</div>
|
|
426
|
+
{% endcall %}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
The `form` object provides a variety of helpers for generating form fields, such as `text_field`, `text_area`, `password_field`, `checkbox`, and more.
|
|
430
|
+
|
|
431
|
+
#### Strong Parameters
|
|
432
|
+
|
|
433
|
+
To prevent mass assignment vulnerabilities, Flaskteroids uses a technique inspired by Rails' **Strong Parameters**. The `params` object allows you to whitelist which parameters are permitted in your controller actions.
|
|
434
|
+
|
|
435
|
+
This is a security best practice that ensures users cannot update model attributes they are not supposed to, such as an admin flag.
|
|
436
|
+
|
|
437
|
+
Here's how you can use `params.expect` to safely handle incoming data. The `expect` method allows you to define the structure of the expected parameters, including nested objects and lists.
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
# app/controllers/posts_controller.py
|
|
441
|
+
from flaskteroids import params
|
|
442
|
+
from flaskteroids.controller import ActionController
|
|
443
|
+
from app.models.post import Post
|
|
444
|
+
|
|
445
|
+
class PostsController(ActionController):
|
|
446
|
+
# ...
|
|
447
|
+
|
|
448
|
+
def create(self):
|
|
449
|
+
self.post = Post.new(_post_params())
|
|
450
|
+
if self.post.save():
|
|
451
|
+
# ... success
|
|
452
|
+
else:
|
|
453
|
+
# ... error
|
|
454
|
+
pass
|
|
455
|
+
|
|
456
|
+
def update(self):
|
|
457
|
+
self.post = Post.find(params['id'])
|
|
458
|
+
if self.post.update(_post_params()):
|
|
459
|
+
# ... success
|
|
460
|
+
else:
|
|
461
|
+
# ... error
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
def _post_params(self):
|
|
465
|
+
# Assumes form data is sent as:
|
|
466
|
+
# post[title]=...
|
|
467
|
+
# post[content]=...
|
|
468
|
+
return params.expect(post=['title', 'content'])
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
In this example, `_post_params` specifies that the `params` object must contain a top-level key called `post`, which should be a dictionary containing only the `title` and `content` keys. Any other attributes within the `post` object will be discarded, and if the `post` key is missing or the structure is incorrect, an exception will be raised.
|
|
473
|
+
|
|
474
|
+
#### CSRF Protection
|
|
475
|
+
|
|
476
|
+
Flaskteroids automatically includes CSRF (Cross-Site Request Forgery) protection on all non-GET requests. A CSRF token is generated and must be included in your forms. The `form_with` helper does this automatically.
|
|
477
|
+
|
|
478
|
+
For manual forms, you can include the token like this:
|
|
479
|
+
|
|
480
|
+
```html
|
|
481
|
+
<form action="/posts" method="post">
|
|
482
|
+
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
483
|
+
<!-- ... form fields -->
|
|
484
|
+
</form>
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Security
|
|
488
|
+
|
|
489
|
+
### Rate Limiting
|
|
490
|
+
|
|
491
|
+
You can easily add rate limiting to your controllers to prevent abuse. The `rate_limit` decorator allows you to specify the maximum number of requests allowed within a given time window.
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
from flaskteroids.rate_limit import rate_limit
|
|
495
|
+
|
|
496
|
+
@rate_limit(to=10, within=60) # 10 requests per 60 seconds
|
|
497
|
+
class PostsController(ActionController):
|
|
498
|
+
# ...
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## Command-Line Interface (CLI)
|
|
502
|
+
|
|
503
|
+
Flaskteroids extends the `flask` CLI with a powerful set of tools to streamline development.
|
|
504
|
+
|
|
505
|
+
### Generators
|
|
506
|
+
|
|
507
|
+
Use the `flask generate` command to create various components:
|
|
508
|
+
|
|
509
|
+
* **Authentication:**
|
|
510
|
+
```sh
|
|
511
|
+
flask generate authentication
|
|
512
|
+
```
|
|
513
|
+
Sets up a complete authentication system (routes, controllers, views).
|
|
514
|
+
|
|
515
|
+
* **Controller:**
|
|
516
|
+
```sh
|
|
517
|
+
flask generate controller <controller_name> [action1 action2 ...]
|
|
518
|
+
```
|
|
519
|
+
*Example:* `flask generate controller users index show`
|
|
520
|
+
|
|
521
|
+
* **Mailer:**
|
|
522
|
+
```sh
|
|
523
|
+
flask generate mailer <mailer_name> [action1 action2 ...]
|
|
524
|
+
```
|
|
525
|
+
*Example:* `flask generate mailer UserMailer welcome_email`
|
|
526
|
+
|
|
527
|
+
* **Migration:**
|
|
528
|
+
```sh
|
|
529
|
+
flask generate migration <migration_name>
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
* **Model:**
|
|
533
|
+
```sh
|
|
534
|
+
flask generate model <model_name> [field1:type ...]
|
|
535
|
+
```
|
|
536
|
+
*Example:* `flask generate model User name:string email:string`
|
|
537
|
+
|
|
538
|
+
* **Resource:**
|
|
539
|
+
```sh
|
|
540
|
+
flask generate resource <resource_name> [field1:type ...]
|
|
541
|
+
```
|
|
542
|
+
Generates a model and a RESTful controller.
|
|
543
|
+
|
|
544
|
+
* **Scaffold:**
|
|
545
|
+
```sh
|
|
546
|
+
flask generate scaffold <scaffold_name> [field1:type ...]
|
|
547
|
+
```
|
|
548
|
+
Generates a model, RESTful controller, and views.
|
|
549
|
+
|
|
550
|
+
## Testing
|
|
551
|
+
|
|
552
|
+
Flaskteroids is designed for testability. To run your application's test suite, use `pytest`:
|
|
553
|
+
|
|
554
|
+
```sh
|
|
555
|
+
pytest
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## Contributing
|
|
559
|
+
|
|
560
|
+
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) to get started.
|
|
561
|
+
|
|
562
|
+
## License
|
|
563
|
+
|
|
564
|
+
Flaskteroids is open-source and released under the [MIT License](LICENSE).
|