├── Dockerfile ├── LICENSE ├── database.db ├── db.py ├── main.py ├── message.sql ├── models.py ├── readme.md ├── requirements.txt ├── schemas.py ├── static ├── Dockerfile ├── favicon.ico ├── message.html └── nginx.conf └── test_api.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | COPY . /app 3 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 4 | RUN echo 'Asia/Shanghai' >/etc/timezone 5 | WORKDIR ./app 6 | RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ 7 | EXPOSE 80 8 | CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 zy7y 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. 22 | -------------------------------------------------------------------------------- /database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zy7y/sayhello/9ab6882581da57e0f5b2523849c5a1f42c5bb5d8/database.db -------------------------------------------------------------------------------- /db.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | 3 | from sqlalchemy.orm import sessionmaker 4 | 5 | engine = create_engine("sqlite:///database.db", connect_args={"check_same_thread": False}) 6 | 7 | SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) 8 | 9 | session = SessionLocal() 10 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from sqlalchemy import func 3 | from starlette.middleware.cors import CORSMiddleware 4 | 5 | from schemas import * 6 | from db import session 7 | import models 8 | 9 | app = FastAPI(title="SayHello(留言板)", 10 | description=""" 11 | 翻自 《Flask Web开发实战_入门、进阶与原理解析(李辉著 )》 中的实战项目SayHello 12 | 原版Github: https://github.com/greyli/sayhello 13 | """ 14 | ) 15 | 16 | # 设置跨域 17 | app.add_middleware( 18 | CORSMiddleware, 19 | allow_origins=["*",], 20 | allow_credentials=True, 21 | allow_methods=["*"], 22 | allow_headers=["*"], 23 | ) 24 | 25 | 26 | @app.get("/index", name="欢迎首页") 27 | async def index(): 28 | return {"msg": "欢迎来到SayHello!"} 29 | 30 | 31 | @app.post("/message", name="添加留言", response_model=Response200) 32 | async def add_message(message: MessageCreate): 33 | message_obj = models.Message( 34 | name=message.name, 35 | body=message.body 36 | ) 37 | session.add(message_obj) 38 | session.commit() 39 | session.refresh(message_obj) 40 | return Response200(data=message_obj) 41 | 42 | 43 | @app.get("/message", name="分页获取留言列表", response_model=ResponseList200) 44 | async def get_messages(limit: int = 5, page: int = 1): 45 | # 统计条数 46 | total = session.query(func.count(models.Message.id)).scalar() 47 | skip = (page - 1) * limit # 计算当前页的起始数 48 | # 倒序显示 49 | data = session.query(models.Message).order_by(models.Message.create_at.desc()).offset(skip).limit(limit).all() 50 | return ResponseList200(total=total, data=data) 51 | -------------------------------------------------------------------------------- /message.sql: -------------------------------------------------------------------------------- 1 | -- 新建数据库,然后执行下面sql 2 | CREATE TABLE message( 3 | id PRIMARY KEY not null, 4 | name VARCHAR(60) not null, 5 | body VARCHAR(200) not null, 6 | create_at DATETIME 7 | ); -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy import Column, String, DateTime, Integer 5 | 6 | # 得到默认Base基类 7 | Base = declarative_base() 8 | 9 | 10 | class Message(Base): 11 | __tablename__ = "message" 12 | id = Column(Integer, primary_key=True, index=True) 13 | name = Column(String(60), comment="昵称", nullable=False) 14 | body = Column(String(200), comment="内容", nullable=False) 15 | create_at = Column(DateTime, default=datetime.now, comment="创建时间") 16 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | > 翻自 《Flask Web开发实战_入门、进阶与原理解析(李辉著 )》 中的实战项目SayHello 3 | **线上体验地址:http://49.232.203.244:9001/message.html** 4 | 5 | # 图片加载不出来 6 | 所有图片都是用的gitee做图床,不知道为什么github展示不出来,需要看图片请前往 7 | [该项目的Gitee仓库地址](https://gitee.com/zy7y/sayhello) 8 | # 技术栈 9 | FastAPI + SQLAlchemy(sqlite3) + html + css + vue.js + axios 10 | # 动态 11 | 1. 新增留言, 留言列表接口, 接口测试 12 | 2. 完善前端页面,更改实时校验,https://blog.csdn.net/qq_22182989/article/details/103728781 13 | 3. 体验版部署,更新docker 部署文档 14 | # 本地启动 15 | 1. 项目目录下执行`pip install -r requirements.txt` 16 | 2. `Terminal(终端)`执行命令`uvicorn main:app --reload` 17 | 3. 访问服务 18 | - http://127.0.0.1:8000/docs # 接口文档 19 | 20 | # docker部署 21 | **详细内容请看:https://www.cnblogs.com/zy7y/p/14344375.html** 22 | 23 | ## 后端部署 24 | 1. 进入到项目目录下(命令请在命令行执行) 25 | 2. 执行`docker build -t sayhello .` 26 | 3. 运行容器`docker run -d --name sayhello-fastapi -p 8000:80 sayhello` 27 | 28 | ## 前端部署 29 | **需要确定static/message.html 中的 `baseURL`地址是不是后端服务器IP地址** 30 |  31 | 1. 进入到项目static目录下 32 | 2. 执行`docker build -t sayhello-front .` 33 | 3. 运行容器`docker run -d --name sayhello-front-9000 -p 9001:80 sayhello-front` 34 | 35 | ## 访问 IP:9001/message.html 36 |  37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | atomicwrites==1.4.0 2 | attrs==20.3.0 3 | certifi==2020.12.5 4 | chardet==4.0.0 5 | click==7.1.2 6 | colorama==0.4.4 7 | fastapi==0.63.0 8 | h11==0.12.0 9 | idna==2.10 10 | iniconfig==1.1.1 11 | packaging==20.8 12 | pluggy==0.13.1 13 | py==1.10.0 14 | pydantic==1.7.3 15 | pyparsing==2.4.7 16 | pytest==6.2.2 17 | requests==2.25.1 18 | SQLAlchemy==1.3.22 19 | starlette==0.13.6 20 | toml==0.10.2 21 | urllib3==1.26.3 22 | uvicorn==0.13.3 23 | -------------------------------------------------------------------------------- /schemas.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from fastapi import Body 4 | from pydantic import BaseModel 5 | 6 | from typing import List 7 | 8 | 9 | class MessageBase(BaseModel): 10 | name: str = Body(..., min_length=2, max_length=8) 11 | body: str = Body(..., min_length=1, max_length=200) 12 | 13 | 14 | class MessageCreate(MessageBase): 15 | pass 16 | 17 | 18 | class Message(MessageBase): 19 | id: int = None 20 | create_at: datetime 21 | 22 | class Config: 23 | orm_mode = True 24 | 25 | 26 | class Response200(BaseModel): 27 | code: int = 200 28 | msg: str = "操作成功" 29 | data: Message = None 30 | 31 | 32 | class ResponseList200(Response200): 33 | total: int 34 | data: List[Message] 35 | 36 | 37 | class Response400(Response200): 38 | code: int = 400 39 | msg: str = "无数据返回" 40 | -------------------------------------------------------------------------------- /static/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.15.2-alpine 2 | COPY . /usr/share/nginx/html 3 | COPY nginx.conf /etc/nginx/conf.d/default.conf 4 | EXPOSE 80 5 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zy7y/sayhello/9ab6882581da57e0f5b2523849c5a1f42c5bb5d8/static/favicon.ico -------------------------------------------------------------------------------- /static/message.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |{{messageTotal}} messages
45 |{{message.body}}
59 |