后端开发历程
*注意 项目仍持续开发中,代码在持续增加或改动。如需实时代码请前往github项目地址。
-
项目环境搭建
我选择pycharm作为IDE,原因是自带virtual开发环境,可视化的包管理器,最重要的一点好看。
项目主要由 api组件和models.py组成, manage.py实现项目启动及部署,config.py实现项目配置。
蓝图及其配置 可参考Blueprint。 -
数据库建立(使用sqlalchemy orm)
其中关系建立请参考这篇文章- 注意点
- uuid生成不能直接 放在字段的 default=uuid.uuid1, 需要str(uuid.uuid1)
- uuid 不能放在default=后, 每次创建一条数据时,会导致uuid重复,使其unque键约束冲突
- datetime.now() 同样不能直接放在default=后,与上一条原因一样
- to_json方法,sqlalchemy没有提供序列化方法,所以我们需要将其查询的数据序列化
from app import db from datetime import datetime from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash import uuid # uuid生成 def gen_uuid(): return uuid.uuid1().hex # 时间日期生成 def gen_time(): return datetime.now() def set_info(body): info = body[:20] s = '' for str in info: s = s + str.strip('#|*`') return s.strip().replace(' ', ',') # 权限 class Permission(db.Model): __tablename__ = 'permission' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), unique=True) url = db.Column(db.String(255), unique=True) role = db.Column(db.Integer, db.ForeignKey('roles.id')) # 所属组 create_time = db.Column(db.DateTime, index=True,) def __init__(self): self.create_time = gen_time() # 标签 class Tag(db.Model): __tablename__ = 'tag' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), unique=True) create_time = db.Column(db.DateTime, index=True) article = db.relationship("Article", backref='tag') def __init__(self): self.create_time = gen_time() # 文章 class Article(db.Model): __tablename__ = 'article' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(128)) info = db.Column(db.Text) body = db.Column(db.Text) body_html = db.Column(db.Text) create_time = db.Column(db.DateTime, index=True) star = db.Column(db.SmallInteger) tag_id = db.Column(db.Integer, db.ForeignKey('tag.id')) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) comments = db.relationship("Comment", backref='article') def __init__(self, title, body, body_html): self.title = title self.body = body self.body_html = body_html self.info = set_info(body) self.create_time = gen_time() # self.kind = kind def __repr__(self): return "<Article %r>" % self.title # json序列 def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict # 评论 class Comment(db.Model): __tablename__ = 'comment' id = db.Column(db.Integer, primary_key=True) content = db.Column(db.Integer) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) article_id = db.Column(db.Integer, db.ForeignKey('article.id')) create_time = db.Column(db.DateTime, index=True) def __init__(self, content, user_id, article_id): self.content = content self.user_id = user_id self.article_id = article_id self.create_time = gen_time() def __repr__(self): return '<Comment %r>' % self.id def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict # 角色 class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) role_name = db.Column(db.String, unique=True) permission = db.relationship("Permission", backref='roles') user = db.relationship('User', backref='roles', lazy='dynamic') @staticmethod def insert_roles(): roles = ['管理员', '普通用户'] for r in roles: role = Role.query.filter_by(role_name=r).first() if role is None: role = Role(role_name=r) db.session.add(role) db.session.commit() def __repr__(self): return '' % (self.id, self.name) # json序列化 def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict # 用户 class User(db.Model, UserMixin): # 表名 __tablename__ = 'user' # 字段 id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(16), unique=True) create_time = db.Column(db.DateTime, index=True) password_hash = db.Column(db.String(128)) face = db.Column(db.String(255), unique=True) uuid = db.Column(db.String(255), unique=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) article = db.relationship('Article', backref='user', lazy='dynamic') user_logs = db.relationship('Userlog', backref='user') # 会员日志外键关系关联 admin_logs = db.relationship('Adminlog', backref='user') # 管理员外键关系关联 comments = db.relationship('Comment', backref='user') # 评论外键关联 def __init__(self, name, password, role): self.name = name self.password_hash = generate_password_hash(password) self.role_id = role self.uuid = gen_uuid() self.create_time = gen_time() @staticmethod def insert_admin(): admin = User.query.filter_by(name='yker').first() if admin is None: admin = User(name='yker', password='yker123', role=1) db.session.add(admin) db.session.commit() def __repr__(self): return '' % (self.id, self.name) # json序列化 def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict def verify_password(self, password): if self.password_hash is None: return False else: return check_password_hash(self.password_hash, password) # 会员登录日志 class Userlog(db.Model): __tablename__ = 'userlog' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) ip = db.Column(db.String(100)) create_time = db.Column(db.DateTime, index=True) def __init__(self): self.create_time = gen_time() def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict # 管理员日志 class Adminlog(db.Model): __tablename__ = 'adminlog' id = db.Column(db.Integer, primary_key=True) admin_id = db.Column(db.Integer, db.ForeignKey('user.id')) ip = db.Column(db.String(100)) create_time = db.Column(db.DateTime, index=True) def __init__(self): self.create_time = gen_time() def __repr__(self): return "<Admin %r>" % self.id def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict # 操作日志 class Oplog(db.Model): __tablename__ = 'oplog' id = db.Column(db.Integer, primary_key=True) admin_id = db.Column(db.Integer, db.ForeignKey('user.id')) ip = db.Column(db.String(100)) reason = db.Column(db.String(600)) create_time = db.Column(db.DateTime, index=True) def __init__(self, reason): self.create_time = gen_time() self.reason = reason def __repr__(self): return "<opmin %r>" % self.id def to_json(self): dict = self.__dict__ if "_sa_instance_state" in dict: del dict["_sa_instance_state"] return dict
- 注意点
-
api接口开发
通常来说,开发前后端分离项目时,需要与前端人员约定api准侧,由于是个人开发,所以跟自己约定下就行。- 注意点
- request 的使用参考 request
- 数据量多时,我们应该进行分页查询
- 多表联合查询时需要先进行group_by
- 查询时需要先order_by再分页
- sqlalchemy 文档请参考 sqlalchemy文档
from flask import jsonify, request from datetime import datetime from sqlalchemy import func from app.models import User, Article, Role, Adminlog, Oplog, Userlog, Comment from . import api from .. import db from manage import app # 管理员 与 普通用户 ADMINISTRATOR = 1 ORDINARY = 2 # 生成log json def gen_json(object): result = [] for obj in object: result.append({'id': obj.id, 'name': obj.name, 'create_time': obj.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'ip': obj.ip}) return result # 登录验证 @api.route('/api/login', methods=['POST']) def login(): user = User.query.filter_by(name=request.form.get('name')).first() password = request.form.get('password') if user is not None and user.verify_password(password=password): user_log = Userlog(user_id=user.id) db.session.add(user_log) db.session.commit() return jsonify({'is_authorization': 'true', 'id': user.id, 'name': request.form.get('name'), 'token': user.uuid, 'status': 200}) # 注册 @api.route('/api/register', methods=['POST']) def register(): name = request.form.get('name') password = request.form.get('password') if name and password: result = User.query.filter_by(name=name).count() if result is not 0: user = User(name=name, password=password, role=ORDINARY) db.session.add(user) db.session.commit() op_log = Oplog(reason='用户注册') db.session.add(op_log) db.session.commit() users = User.query.filter_by(name=request.form.get('name')).first() return jsonify({'is_authorization': 'true', 'name': users.name, 'token': users.uuid, 'status': 200}) return jsonify({'flag': 'error', 'reason': '该账号已经注册', 'status': 400}) return jsonify({'flag': 'error', 'status': 400}) # 用户api 增删改查 @api.route('/api/user', methods=['GET']) def get_user(): page_size = request.args.get('page_size') users = User.query.filter_by(role_id=ORDINARY).\ outerjoin(Role).add_columns(User.id, User.name, User.uuid, User.create_time, Role.role_name).\ paginate(int(page_size), per_page=10, error_out=False) result = [] for user in users.items: result.append({'id': user.id, 'name': user.name, 'role': user.role_name, 'create_time': user.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'uuid': user.uuid}) return jsonify({'userData': result, 'user_total': users.total, 'status': 200}) @api.route('/api/user', methods=['POST']) def add_user(): if not request.json or not 'name' in request.json or not 'password' in request.json: return jsonify({'status': 400}) name = request.json['name'] password = request.json['password'] result = User.query.filter_by(name=name).count() if result is not 0: return jsonify({'flag': 'error', 'reason': '该账号已经注册', 'status': 400}) user = User(name=name, password=password, role=ORDINARY) db.session.add(user) db.session.commit() op_log = Oplog(reason='添加用户') db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) @api.route('/api/user', methods=['PUT']) def update_user(): pass @api.route('/api/user/<int:id>', methods=['DELETE']) def delete_user(id): if id is None and User.query.filter_by(id=id).first() is None: return jsonify({'status': 400}) user = User.query.filter_by(id=id).first() db.session.delete(user) op_log = Oplog(reason='删除用户') db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 文章api # 获得所有文章 @api.route('/api/article', methods=['GET']) def get_all_article(): page_size = request.args.get('page_size') articles = db.session.query(Article, func.count(Comment.article_id). label('number')).outerjoin(Comment).\ group_by(Article.id).add_columns(Article.id, Article.title, Article.create_time, Article.info, Article.star). \ paginate(int(page_size), per_page=4, error_out=False) result = [] for article in articles.items: result.append({ 'id': article.id, 'title': article.title, 'info': article.info, 'star': article.star, 'number': article.number, 'create_time': article.create_time.strftime("%Y-%m-%d %H:%M:%S") }) return jsonify({'articleList': result, 'total': articles.total, 'status': 200}) # 根据文章id获取文章 @api.route('/api/article/<int:id>', methods=['GET']) def get_article(id): if id is None and Article.query.filter_by(id=id).first() is None: return jsonify({'status': 400}) articles = Article.query.filter_by(id=id).add_columns(Article.id, Article.title, Article.create_time, Article.body_html) result = [] for article in articles: result.append({'id': article.id, 'title': article.title, 'create_time': article.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'html': article.body_html}) return jsonify({'articleContent': result, 'status': 200}) # 添加文章 @api.route('/api/article', methods=['POST']) def add_article(): if not request.json or not 'title' in request.json or not 'body' in request.json \ or not 'body_html' in request.json: return jsonify({'status': 400}) article = Article(title=request.json['title'], body=request.json['body'], body_html=request.json['body_html']) op_log = Oplog(reason='发布文章') db.session.add(article) db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 更新文章 @api.route('/api/article/<int:id>', methods=['put']) def update_article(): pass @api.route('/api/article/<int:id>', methods=['DELETE']) def delete_article(id): if id is None and Article.query.filter_by(id=id).first() is None: return jsonify({'status': 400}) article = Article.query.filter_by(id=id).first() op_log = Oplog(reason='删除文章') db.session.delete(article) db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 角色设置 @api.route('/api/role', methods=['GET']) def get_role(): roles = Role.query.all() return jsonify({'roleData': [role.to_json() for role in roles], 'status': 200}) @api.route('/api/role', methods=['POST']) def add_role(): name = request.json['name'] if name is not None: role = Role(role_name=name) db.session.add(role) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) else: return jsonify({'status': 400}) @api.route('/api/role/<int:id>', methods=['DELETE']) def delete_role(id): if id is None and Role.query.filter_by(id=id).first() is None: return jsonify({'status': 400}) role = Role.query.filter_by(id=id).first() db.session.delete(role) op_log = Oplog(reason='删除角色') db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 管理员 @api.route('/api/admin', methods=['GET']) def get_admin(): page_size = request.args.get('page_size') admins = User.query.filter_by(role_id=ADMINISTRATOR).\ outerjoin(Role).add_columns(User.id, User.name, User.uuid, User.create_time, Role.role_name).\ paginate(int(page_size), per_page=10, error_out=False) result = [] for admin in admins.items: result.append({'id': admin.id, 'name': admin.name, 'role': admin.role_name, 'create_time': admin.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'uuid': admin.uuid}) return jsonify({'adminData': result, 'admin_total': admins.total, 'status': 200}) @api.route('/api/admin', methods=['POST']) def add_admin(): if not request.json or not 'name' in request.json or not 'password' in request.json: return jsonify({'status': 400}) name = request.json['name'] password = request.json['password'] result = User.query.filter_by(name=name).count() if result is not 0: return jsonify({'flag': 'error', 'reason': '该账号已经注册', 'status': 400}) admin = User(name=name, password=password, role=ADMINISTRATOR) db.session.add(admin) db.session.commit() op_log = Oplog(reason='添加管理员') db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200, }) @api.route('/api/admin/<int:id>', methods=['DELETE']) def delete_admin(id): if id is None and User.query.filter_by(id=id).first() is None: return jsonify({'status': 400}) admin = User.query.filter_by(id=id).first() db.session.delete(admin) op_log = Oplog(reason='删除管理员') db.session.add(op_log) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 评论 @api.route('/api/comment', methods=['GET']) def get_comment(): article_id = request.args.get('article') page_size = request.args.get('page_size') if page_size is None and Comment.query.filter_by(article_id=article_id).all() is None: return jsonify({'status': 400}) comments = Comment.query.filter_by(article_id=article_id).outerjoin(User)\ .add_columns( Comment.content, Comment.create_time, User.name, User.face ).order_by(Comment.create_time.desc()).paginate(int(page_size), per_page=20, error_out=False) result = [] for comment in comments.items: result.append({'author': comment.name, 'content': comment.content, 'avatar': comment.face, 'create_time': comment.create_time.strftime("%Y-%m-%d %H:%M:%S")}) return jsonify({'comment': result, 'comment_total': comments.total, 'status': 200}) @api.route('/api/comment', methods=['POST']) def add_comment(): if request.json['id'] and \ request.json['content'] and request.json['article_id'] is None: return jsonify({'status': 400}) comment = Comment(content=request.json['content'], user_id=request.json['id'], article_id=request.json['article_id']) db.session.add(comment) db.session.commit() return jsonify({'flag': 'success', 'status': 200}) # 权限管理 # 日志 @api.route('/api/admin_log', methods=['GET']) def get_admin_log(): page_size = request.args.get('page_size') logs = Adminlog.query.outerjoin(User).add_columns(Adminlog.id, User.name, Adminlog.ip, Adminlog.create_time).\ paginate(int(page_size), per_page=10, error_out=False) return jsonify({'AdminLog': gen_json(logs.items), 'adminLog_total': logs.total, 'status': 200}) @api.route('/api/user_log', methods=['GET']) def get_user_log(): page_size = request.args.get('page_size') logs = Userlog.query.outerjoin(User).add_columns(Userlog.id, User.name, Userlog.ip, Userlog.create_time).\ paginate(int(page_size), per_page=10, error_out=False) return jsonify({'UserLog': gen_json(logs.items), 'userLog_total': logs.total, 'status': 200}) @api.route('/api/op_log', methods=['GET']) def get_op_log(): page_size = request.args.get('page_size') logs = Oplog.query.outerjoin(User).add_columns(Oplog.id, User.name, Oplog.ip, Oplog.create_time, Oplog.reason).\ paginate(int(page_size), per_page=10, error_out=False) result = [] for obj in logs.items: result.append({'id': obj.id, 'name': obj.name, 'create_time': obj.create_time.strftime("%Y-%m-%d %H:%M:%S"), 'ip': obj.ip, 'reason': obj.reason}) return jsonify({'OpLog': result, 'op_total': logs.total, 'status': 200})
- 注意点