flask项目
Posted PythonKnowledge
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flask项目相关的知识,希望对你有一定的参考价值。
前期准备
环境搭建
git的作用
1、方便多人协同开发
2、版本控制
与SVN相比特点
svn(集中式版本控制系统)
1、服务端与客户端都有版本控制能力(集中式的客户端没有版本控制能力),都能进行代码提交合并
2、客户端有本地代码仓库(在当前项目的.git文件内,保存本地的版本记录)
工作区 暂存区 仓库区
git命令
创建git仓库并添加忽略文件
# 初始化 会自动创建.git、.idea文件夹
git init
# 克隆文件
git clone 路径
# 查看当前文件状态
git status
# 将文件添加至缓存区
git add .(git add 要添加的文件名)
# 添加修改人的信息
git congif user.name "陈东发"
git config user.emai "chendongfa@163.com"
# 将文件上传到本地的代码仓库
git commit -m "修改的信息"
# 将文件上传至服务器代码仓库
git push 路径
创建忽略文件
创建.gitignore 里面保存不用上传的文件
.idea/
*.py[cod]
回退版本与撤销修改
尽量使用ctrl + Z
没有 add .(直接从仓库区直接下载下来,覆盖当前的文件)
git checkout 文件名
有 add .但没有 commit(先将暂存区代码撤销到工作区,在覆盖文件)
git reset HEAD 文件名
git checkout 文件名
已经commit
git reset --hard 版本号
git reset --hard HEAD(尖括号)
对比版本
git diff HEAD HEAD^ -- login.py
删除
rm 文件名(从工作目录删除文件)
git rm 文件名(从仓库区删除文件)
误删处理
从仓库区下载
git checkout 文件名
远程仓库
使用github或者码云自己搭建服务器
使用github或者码云自己搭建服务器
标签
作用 记录大版本、备份大版本代码
git tag -a 标签名 -m '标签描述'
git tag -a v1.0 -m 'version 1.0'
分支
应用场景:1、开发时不影响主分支上的代码运行 2、 开发测试新功能
主分支主要保存 生产环境代码 主分支不进行代码开发
分支创建 代码合并提交
// 查看分支
git branch
// 创建分支
checkout -b dev(分支名)
// 查看分支
git branch
//提交分支
git push -u origin dev
// 提交编辑过的分支内容
git push
// 合并到主分支(1、切换至主分支 2、合并到主分支 3、提交)
git checkout master
git merge dev
git push
数据库创建
数据表分析
数据表模型创建
models.py(数据表模型)
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from info import constants
from . import db
class BaseModel(object):
"""模型基类,为每个模型补充创建时间与更新时间"""
create_time = db.Column(db.DateTime, default=datetime.now) # 记录的创建时间
update_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) # 记录的更新时间
# 用户收藏表,建立用户与其收藏新闻多对多的关系
tb_user_collection = db.Table(
"info_user_collection",
db.Column("user_id", db.Integer, db.ForeignKey("info_user.id"), primary_key=True), # 新闻编号
db.Column("news_id", db.Integer, db.ForeignKey("info_news.id"), primary_key=True), # 分类编号
db.Column("create_time", db.DateTime, default=datetime.now) # 收藏创建时间
)
tb_user_follows = db.Table(
"info_user_fans",
db.Column('follower_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True), # 粉丝id
db.Column('followed_id', db.Integer, db.ForeignKey('info_user.id'), primary_key=True) # 被关注人的id
)
class User(BaseModel, db.Model):
"""用户"""
__tablename__ = "info_user"
id = db.Column(db.Integer, primary_key=True) # 用户编号
nick_name = db.Column(db.String(32), unique=True, nullable=False) # 用户昵称
password_hash = db.Column(db.String(128), nullable=False) # 加密的密码
mobile = db.Column(db.String(11), unique=True, nullable=False) # 手机号
avatar_url = db.Column(db.String(256)) # 用户头像路径
last_login = db.Column(db.DateTime, default=datetime.now) # 最后一次登录时间
is_admin = db.Column(db.Boolean, default=False)
signature = db.Column(db.String(512)) # 用户签名
gender = db.Column( # 订单的状态
db.Enum(
"MAN", # 男
"WOMAN" # 女
),
default="MAN")
# 当前用户收藏的所有新闻lazy="dynamic"
collection_news = db.relationship("News", secondary=tb_user_collection, lazy="dynamic") # 用户收藏的新闻
# 用户所有的粉丝,添加了反向引用followed,代表用户都关注了哪些人
followers = db.relationship('User',
secondary=tb_user_follows,
primaryjoin=id == tb_user_follows.c.followed_id,
secondaryjoin=id == tb_user_follows.c.follower_id,
backref=db.backref('followed', lazy='dynamic'),
lazy='dynamic')
@property
def password(self):
raise AttributeError("当前属性不允许读取")
@password.setter
def password(self, value):
# self.password_hash = 对value加密
self.password_hash = generate_password_hash(value)
def check_password(self, password):
"""校验密码"""
return check_password_hash(self.password_hash, password)
# 当前用户所发布的新闻
news_list = db.relationship('News', backref='user', lazy='dynamic')
def to_dict(self):
resp_dict = {
"id": self.id,
"nick_name": self.nick_name,
"avatar_url": constants.QINIU_DOMIN_PREFIX + self.avatar_url if self.avatar_url else "",
"mobile": self.mobile,
"gender": self.gender if self.gender else "MAN",
"signature": self.signature if self.signature else "",
"followers_count": self.followers.count(),
"news_count": self.news_list.count()
}
return resp_dict
def to_admin_dict(self):
resp_dict = {
"id": self.id,
"nick_name": self.nick_name,
"mobile": self.mobile,
"register": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"last_login": self.last_login.strftime("%Y-%m-%d %H:%M:%S"),
}
return resp_dict
class News(BaseModel, db.Model):
"""新闻"""
__tablename__ = "info_news"
id = db.Column(db.Integer, primary_key=True) # 新闻编号
title = db.Column(db.String(256), nullable=False) # 新闻标题
source = db.Column(db.String(64), nullable=False) # 新闻来源
digest = db.Column(db.String(512), nullable=False) # 新闻摘要
content = db.Column(db.Text, nullable=False) # 新闻内容
clicks = db.Column(db.Integer, default=0) # 浏览量
index_image_url = db.Column(db.String(256)) # 新闻列表图片路径
category_id = db.Column(db.Integer, db.ForeignKey("info_category.id"))
user_id = db.Column(db.Integer, db.ForeignKey("info_user.id")) # 当前新闻的作者id
status = db.Column(db.Integer, default=0) # 当前新闻状态 如果为0代表审核通过,1代表审核中,-1代表审核不通过
reason = db.Column(db.String(256)) # 未通过原因,status = -1 的时候使用
# 当前新闻的所有评论
comments = db.relationship("Comment", lazy="dynamic")
def to_review_dict(self):
resp_dict = {
"id": self.id,
"title": self.title,
"create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"status": self.status,
"reason": self.reason if self.reason else ""
}
return resp_dict
def to_basic_dict(self):
resp_dict = {
"id": self.id,
"title": self.title,
"source": self.source,
"digest": self.digest,
"create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"index_image_url": self.index_image_url,
"clicks": self.clicks,
}
return resp_dict
def to_dict(self):
resp_dict = {
"id": self.id,
"title": self.title,
"source": self.source,
"digest": self.digest,
"create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"content": self.content,
"comments_count": self.comments.count(),
"clicks": self.clicks,
"category": self.category.to_dict(),
"index_image_url": self.index_image_url,
"author": self.user.to_dict() if self.user else None
}
return resp_dict
class Comment(BaseModel, db.Model):
"""评论"""
__tablename__ = "info_comment"
id = db.Column(db.Integer, primary_key=True) # 评论编号
user_id = db.Column(db.Integer, db.ForeignKey("info_user.id"), nullable=False) # 用户id
news_id = db.Column(db.Integer, db.ForeignKey("info_news.id"), nullable=False) # 新闻id
content = db.Column(db.Text, nullable=False) # 评论内容
parent_id = db.Column(db.Integer, db.ForeignKey("info_comment.id")) # 父评论id
parent = db.relationship("Comment", remote_side=[id]) # 自关联
like_count = db.Column(db.Integer, default=0) # 点赞条数
def to_dict(self):
resp_dict = {
"id": self.id,
"create_time": self.create_time.strftime("%Y-%m-%d %H:%M:%S"),
"content": self.content,
"parent": self.parent.to_dict() if self.parent else None,
"user": User.query.get(self.user_id).to_dict(),
"news_id": self.news_id,
"like_count": self.like_count
}
return resp_dict
class CommentLike(BaseModel, db.Model):
"""评论点赞"""
__tablename__ = "info_comment_like"
comment_id = db.Column("comment_id", db.Integer, db.ForeignKey("info_comment.id"), primary_key=True) # 评论编号
user_id = db.Column("user_id", db.Integer, db.ForeignKey("info_user.id"), primary_key=True) # 用户编号
class Category(BaseModel, db.Model):
"""新闻分类"""
__tablename__ = "info_category"
id = db.Column(db.Integer, primary_key=True) # 分类编号
name = db.Column(db.String(64), nullable=False) # 分类名
news_list = db.relationship('News', backref='category', lazy='dynamic')
def to_dict(self):
resp_dict = {
"id": self.id,
"name": self.name
}
return resp_dict
constants.py(数据库的常量)
# 图片验证码Redis有效期, 单位:秒
IMAGE_CODE_REDIS_EXPIRES = 300
# 短信验证码Redis有效期,单位:秒
SMS_CODE_REDIS_EXPIRES = 300
# 七牛空间域名
QINIU_DOMIN_PREFIX = "http://oyucyko3w.bkt.clouddn.com/"
# 首页展示最多的新闻数量
HOME_PAGE_MAX_NEWS = 10
# 用户的关注每一页最多数量
USER_FOLLOWED_MAX_COUNT = 4
# 用户收藏显示最多新闻数量
USER_COLLECTION_MAX_NEWS = 10
# 其他用户每一页最多新闻数量
OTHER_NEWS_PAGE_MAX_COUNT = 10
# 点击排行展示的最多新闻数据
CLICK_RANK_MAX_NEWS = 6
# 管理员页面用户每页多最数据条数
ADMIN_USER_PAGE_MAX_COUNT = 10
# 管理员页面新闻每页多最数据条数
ADMIN_NEWS_PAGE_MAX_COUNT = 10
前期配置
manage.py
# 导入logging模块 不是flask中的logging模块
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from redis import StrictRedis
from flask_session import Session
from flask_script import Manager
from flask_migrate import MigrateCommand
from flask_migrate import Migrate
class Config(object):
"""项目配置"""
# app的配置
DEBUG = True
# mysql 的配置
SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/gitee"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# redis的配置
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# Session保存位置配置
SESSION_TYPE = "redis"
# 开启session签名
SESSION_USE_SIGNER = True
# 指定 Session 保存的 redis
SESSION_REDIS = StrictRedis(host=REDIS_HOST, port=REDIS_PORT)
# 设置需要过期
SESSION_PERMANENT = False
# 设置过期时间
PERMANENT_SESSION_LIFETIME = 86400 * 2
# 要使用session必须要设置secrect_key
SECRET_KEY = "iECgbYWReMNxkRprrzMo5KAQYnb2UeZ3bwvReTSt+VSESW0OB8zbglT+6rEcDW9X"
# 设置日志的记录等级
logging.basicConfig(level=logging.DEBUG) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限。需要再当前文件夹中穿件log文件夹,并且再文件夹内创建 .keepgit文件,要保证logs文件夹都能长传
file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 100, backupCount=10)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flask app使用的)添加日志记录器
logging.getLogger().addHandler(file_log_handler)
app = Flask(__name__)
app.config.from_object(Config)
# 初始化db
db = SQLAlchemy(app)
# 初始化redis
redis_store = StrictRedis(host=Config.REDIS_HOST,port=Config.REDIS_PORT)
# 开启当前的CSRF保护,只做服务器验证功能
CSRFProtect(app)
# 设置session的保存位置,执行下面的代码就会去app的设置中查询session的设置
Session(app)
# 生成主函数的manager对象
manager = Manager(app)
# 数据库迁移(当数据库中没有数据表时,可以直接使用数据库迁移命令进行数据库内数据表的生成)
# 首先将数据库与app进行关联(注意先传app,再传db)
Migrate(app, db)
# 然后将迁移命令添加到manager中,这样就可以在命令行中进行命令创建数据库(这里的db是给迁移命令起的名字,可以任意起名字,不过我们默认起名字为db)
manager.add_command("db", MigrateCommand)
@app.route("/")
def index():
session["name"] = "陈东发"
logging.debug("测试debug")
return "index"
if __name__ == "__main__":
manager.run()
配置说明
配置一共有六个部分
1、初始化app以及app配置
2、初始化db以及db配置
3、初始化redisstore以及redisstore配置
4、开启CSRF保护
5、设置session 以及session配置
6、 集成命令行扩展
7、 log日志记录软件的运行状态,选择级别后 输出以选择级别和级别以上的日志进行输出,输出也可以使用current_app.logger.error("测试error"),如果开启debug 级别则是debug如果没有开启,则 输出的级别是设置级别
抽取文件
logic_operation.py
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from redis import StrictRedis
from config import config
# 初始化db
# 在很多flask扩展里面,可以先初始化扩展的对象,然后调用init_app方法绑定app
db = SQLAlchemy()
# 在指定为空,没有只能提示方法,可以先加一个变量注释
redis_store = None # type:StrictRedis
# 或者使用
# redis_store:StrictRedis = None
# https://www.cnblogs.com/xieqiankun/p/type_hints_in_python3.html
def set_log(config_name):
"""log生成以及配置"""
# 设置日志的记录等级
logging.basicConfig(level=config[config_name].LOG_LEVEL) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
file_log_handler = RotatingFileHandler("logs/log", maxBytes=1024 * 1024 * 100, backupCount=10)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter('%(levelname)s %(filename)s:%(lineno)d %(message)s')
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flask app使用的)添加日志记录器
logging.getLogger().addHandler(file_log_handler)
def create_app(config_name):
"""创建falsk对象"""
# 进行log的配置
set_log(config_name)
app = Flask(__name__)
app.config.from_object(config[config_name])
# 通过app初始化
db.init_app(app)
# 初始化redis
# 先设置为空,再使用全局变量声明,去进行设置
global redis_store
redis_store = StrictRedis(host=config[config_name].REDIS_HOST, port=config[config_name].REDIS_PORT)
# 开启当前的CSRF保护,只做服务器验证功能
CSRFProtect(app)
# 设置session的保存位置,执行下面的代码就会去app的设置中查询session的设置
Session(app)
# 注册蓝图时,什么时候注册,什么时候导入
from info.modules.index.views import index_blu
# 3、注册蓝图路由
app.register_blueprint(index_blu)
return app
manage.py
from flask_script import Manager
from flask_migrate import MigrateCommand
from flask_migrate import Migrate
from info.logic_operation import create_app
from info.logic_operation import db
from info.logic_operation import models
# 通过指定的运行环境,创建flask对象
app = create_app("development")
# 生成主函数的manager对象
manager = Manager(app)
# 数据库迁移(当数据库中没有数据表时,可以直接使用数据库迁移命令进行数据库内数据表的生成)
# 首先将数据库与app进行关联(注意先传app,再传db)
Migrate(app, db)
# 然后将迁移命令添加到manager中,这样就可以在命令行中进行命令创建数据库(这里的db是给迁移命令起的名字,可以任意起名字,不过我们默认起名字为db)
manager.add_command("db", MigrateCommand)
if __name__ == "__main__":
manager.run()
config.py
import logging
from redis import StrictRedis
class Config(object):
"""项目配置"""
# app的配置
DEBUG = True
# mysql 的配置
SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/gitee"
SQLALCHEMY_TRACK_MODIFICATIONS = False
# redis的配置
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# Session保存位置配置
SESSION_TYPE = "redis"
# 开启session签名
SESSION_USE_SIGNER = True
# 指定 Session 保存的 redis
SESSION_REDIS = StrictRedis(host=REDIS_HOST, port=REDIS_PORT)
# 设置需要过期
SESSION_PERMANENT = False
# 设置过期时间
PERMANENT_SESSION_LIFETIME = 86400 * 2
# 要使用session必须要设置secret_key
SECRET_KEY = "iECgbYWReMNxkRprrzMo5KAQYnb2UeZ3bwvReTSt+VSESW0OB8zbglT+6rEcDW9X"
# 设置日志等级
LOG_LEVEL = logging.DEBUG
class DevelopmentConfig(Config):
"""开发环境下的配置"""
DEBUG = True
class ProductionConfig(Config):
"""生产环境下的配置"""
DEBUG = False
LOG_LEVEL = logging.WARNING
class TestingConfig(Config):
"""调试环境下的配置"""
DEBUG = True
TESTING = True
# 将环境配置以字典的形式保存,方便后序使用
config = {
"development": DevelopmentConfig,
"productiont": ProductionConfig,
"testing": TestingConfig
}
.gitignore
.idea/
*.py[cod]
logs/log*
views.py
import logging
# 1、导包
from flask import Blueprint, session, current_app
# 2、创建蓝图对象
from info.logic_operation import redis_store
index_blu = Blueprint("index",__name__)
@index_blu.route("/")
def index():
session["name"] = "陈东发"
session["class"] = "python3期"
# 测试打印日志
logging.warning("这是调试日志 warning")
logging.debug("这是调试日志")
# 或者使用
# 默认debug,再app中设置啥就是啥
current_app.logger.error("测试日志")
redis_store.set("name", "chendongfa")
return "index"
抽取说明
入口函数文件(manage.py):
只保存程序的运行(app的创建拿到manage.py中的原因是:入口函数文件的主要功能就是程序的开启,开启需要指定程序运行的环境,因此可以在业务逻辑文件中创建一个app的生成函数,将运行环境当作形参传递,这样在入口函数中传入实参(函数的运行环境)即可初始化)
业务逻辑文件:
保存业务逻辑的文件,即主要的执行文件。
其中的初始化业务逻辑文件中,主要有app初始化函数,db初始化(可以先进行初始化,然后再调用initapp方法绑定app),redisstore初始化,log日志的初始化
配置文件:保存关于配置的信息。
视图函数文件:使用蓝图管理(蓝图的使用步骤1、导包(Bluprint)2、创建蓝图对象3、注册蓝图路由)
数据库迁移
操作数据库 安装模块
pip install flask-sqlalchemy
pip install mysqlclient==1.3.12
pip install flask-mysqldb
# 1、 创建迁移文件夹(创建的迁移文件夹不用上传,忽略文件中添加)
python manage.py db init
# 2、创建当前版本的迁移文件(在manage.py中导入先前创建的数据库模型文件models.py)
python manage.py db migrate -m "导入信息"
# 3、创建数据库
create database gitee charset=uft8
# 4、 执行迁移
python manage.py db upgrade
添加测试数据
set names utf8
source info_info_category.sql
# 先添加新闻分类一方 再添加多方
导入静态文件
导入前端的文件
以上是关于flask项目的主要内容,如果未能解决你的问题,请参考以下文章