创建一个TODO应用程序是熟悉某些核心编程技术的好方法。在这篇文章中,我展示了一个示例Python项目,该项目使用烧瓶,PostgreSQL和Docker。该代码可在此GitHub repo中使用,您可以将其用作创建自己的应用程序的灵感。
使用的技术
首先,我想简要介绍我使用的技术,并解释原因:
是什么:一个轻量级的Web框架,可以用最少的代码快速构建Web应用程序。
为什么要使用它:易于开始且灵活。我喜欢学习,而不是为您提供一个严格的框架,它使您可以批判性地思考自己选择的结构。
为什么使用它:它适用于处理大型数据集并为许多不同的数据类型提供支持。
是什么:一个平台,可让您在容器中构建软件。一个容器可以容纳所有内容以运行您的应用程序,包括代码,运行时,系统工具和库。这使得在不同的服务器上运行并随着应用程序的增长而进行协作变得更加容易。
为什么使用它:它可以确保您的应用程序在任何地方(本地机器,虚拟机等)运行相同的运行,这使您更容易扩展应用程序并协作。
应用程序:
该应用程序是一个API,可让您创建,更新和删除Todos。首先,您使您可以使用密码登录并可以与他们的待办事项列表进行交互。
首先,我将展示一个API端点文档中的示例,然后我将向您展示我的项目的结构,其中有一个简短的解释和您自己设计的一些技巧。
API端点
用户路线
-
创建新用户
- url:
/api/users
- 方法:
POST
- 数据参数:
- url:
{
"first_name": "[string]", // Non-empty, max 50 chars, only alpha
"last_name": "[string]", // Non-empty, max 50 chars, only alpha
"email": "[unique string]", // Valid email with '@'
"password": "[string]" // Contains at least 5 chars, 1 number and 1 special char
}
- 成功回应:
- Code:
201 CREATED
- 内容:
{ id: [integer], first_name: "[string]", last_name: "[string]", email: "[unique string]" }
- 错误响应:
- Code:
409 CONFLICT
- Code:
有关可用端点的完整文档,请参见app readme。本质上,这些让用户:
- 创建更新并删除用户
- 创建更新并删除todos
- 获取用于登录的令牌
项目结构
ð simple_todo_app
-
app/:应用程序的核心。
- ð__init__.py
:初始路线和DB- ð auth/:处理身份验证。
- ð
auth.py
:用户身份验证逻辑。
- ð
- ð config/:配置设置。
- ð
config.py
:用于访问env变量。
- ð
- ð crud/: crud操作。
- ð
todo_crud.py
:管理与db的待办事项互动。 - ð
user_crud.py
:管理用户交互。
- ð
- ð帮助者/:使用
- ð
extensions.py
:用于初始化ORM并停止循环进口。 - ð
helpers.py
:常规功能。 - ð
validators.py
:验证数据。
- ð
- ð模型/:数据模型。
- ð
models.py
:ORM模型(使用对象来管理DB相互作用)。
- ð
- ð路由/: url路由。
- ð
routes.py
:地图URL路由到函数。
- ð
- ð auth/:处理身份验证。
-
迁移/:数据库迁移。
-
ð dockerfile :容器化应用程序。
-
ð app.py :应用程序的输入点。
态
-
ð要求.txt :项目依赖项。
解释
该应用程序并未严格遵循MVC设计模式,但是我尝试在可能的情况下将不同的组件分开。它遵循一个基本结构,其中ðapp.py调用ðApp/init.py,此文件初始化了根和db。
app.py
from app import create_app
app = create_app()
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True, port=8001)
app/init.py
from flask import Flask
from flask_migrate import Migrate
from flasgger import Swagger
from app.config.config import Config
from app.helpers.extensions import db, ma
from app.models.models import User, Todo
from app.routes.routes import init_user_routes, init_todo_routes, init_auth_routes
def create_app():
"""Create app instance"""
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app) # Initalize db with app
ma.init_app(app) # Initalize marshamallow with app
migrate = Migrate(app, db) # Migrate db with app
Swagger(app) # Will be used to create OpenAPI documentation
with app.app_context():
db.create_all() # Create all db tables
init_user_routes(app)
init_todo_routes(app)
init_auth_routes(app)
return app
ðApp/ auth/ dir处理用户的授权和身份验证。 ðApp/config保留了用于连接到数据库的一些信息(基本上是在那里保持代码整洁)。 ðApp/crud包含直接读写DB(用于用户和毒品)的功能。 ðApp/helpers是一个通用文件夹,包含一些功能和额外的功能。 ð应用程序/模型包含用于与DB交互的Python对象(这是ORM,如下所述)。
app/models/models.py
from marshmallow import fields
from app.helpers.extensions import db, ma
class Todo(db.Model):
"""Todo table containing todo items for users"""
__tablename__ = "todo"
id = db.Column(db.Integer, primary_key=True)
user_email = db.Column(db.String, db.ForeignKey("user.email"))
content = db.Column(db.String, nullable=False)
priority = db.Column(db.Integer, nullable=False, default=1)
class TodoSchema(ma.SQLAlchemyAutoSchema):
"""TodoSchema for serializing and deserializing Todo instances"""
class Meta:
model = Todo
load_instance = True # Deserialize to model instance
sqla_Session = db.session
include_fk = True # So marshmallow recognises person_id during serialization
user_email = fields.Str()
content = fields.Str()
priority = fields.Integer()
class User(db.Model):
"""User table containing user details"""
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50), unique=True)
first_name = db.Column(db.String(50))
last_name = db.Column(db.String(50))
password = db.Column(db.String, nullable=False)
todos = db.relationship(
Todo,
backref="user",
cascade="all, delete, delete-orphan",
single_parent=True
)
def __repr__(self):
return f"User {self.first_name} {self.last_name} with email {self.email}"
class UserLoadSchema(ma.SQLAlchemyAutoSchema):
"""UserLoadSchema for deserializing User instances"""
class Meta:
model = User
load_instance = True
sqla_Session = db.session
include_relationships = True # This means it will also go into neighbouring schema
exclude = ("id", "password") # Exclude password and id during deserialization
email = fields.Str()
first_name = fields.Str()
last_name = fields.Str()
password = fields.Str() # Password needed for user load
todos = fields.Nested(TodoSchema, many=True)
class UserDumpSchema(ma.SQLAlchemyAutoSchema):
"""UserDumpSchema for serializing User instances"""
class Meta:
model = User
load_instance = True # Deserialize to model instance
sqla_Session = db.session
include_relationships = True
exclude = ("id", "password")
email = fields.Str()
first_name = fields.Str()
last_name = fields.Str()
todos = fields.Nested(TodoSchema, many=True)
# Initialized schemas for global use throughout the app
todo_schema = TodoSchema()
todos_schema = TodoSchema(many=True) # Many=True to serialize a list of objects
user_load_schema = UserLoadSchema() # Used for deserializing user data from requests
user_dump_schema = UserDumpSchema() # Used for serializing user data to responses
根文件夹中的迁移用于数据库迁移(基本上是DB的版本控制)。 ðdocker-compose.yml用于定义应用两个容器之间的关系(一个用于烧瓶应用程序,一个用于PostgreSQL DB)。 ðdockerfile指定了烧瓶应用程序的详细信息(PostgreSQL没有一个详细信息,因为这是在标准的PostgreSQL Docker Image上构建的)。 ðsupport.txt文件定义了烧瓶应用所需的软件包。
docker-compose.yml
version: '3.8'
services:
web:
build: .
volumes:
- .:/app
ports:
- "8001:8001"
depends_on:
- pgsql
environment:
- FLASK_APP=app:create_app
- FLASK_RUN_HOST=0.0.0.0
pgsql:
image: postgres:12.11
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data/
env_file:
- .env
ports:
- 5432:5432
volumes:
postgres_data:
您的设计中要考虑的事情
- orm - 该应用使用对象凝聚的映射,从而简化了面向对象的语言(例如Python)和数据库之间的相互作用。有关正确的解释,请参见free code camps explanation
-
数据库越来越
- 结构 - 正如我上面提到的,我尝试将不同的应用程序组件分开。通常,这使您的应用程序更易于调试,维护和扩展。
- 棉花糖 - 是一个用于序列化/测试化(转换数据类型)的Python库,也进行数据验证
我希望您喜欢这个帖子,并为自己的项目启发您!