項目描述
該項目實現電影系統的后台接口,包括用戶,電影,場次,訂單,評論,優惠券,推薦,收藏等多個模塊,同時提供一個管理后台進行操作和管理。使用Swagger使用接口的可視化,方便測試。
項目截圖
項目結構
MovieAdmin
├── requirements.txt # 第三方庫依賴文件
└── src
├── app
│ ├── admin # 管理后台文件
│ │ ├── admin.py
│ │ ├── __init__.py
│ │ └── views.py
│ ├── api # 接口文件
│ │ ├── comment.py
│ │ ├── coupon.py
│ │ ├── favorite.py
│ │ ├── __init__.py
│ │ ├── movie.py
│ │ ├── order.py
│ │ ├── password.py
│ │ ├── screen.py
│ │ ├── session.py
│ │ └── user.py
│ ├── __init__.py
│ ├── models.py # 數據庫模型
│ ├── static # 靜態文件,保存用戶頭像和電影海報等
│ │ ├── images
│ │ │ ├── poster
│ │ │ └── user
│ │ │ └── default.jpg
│ │ └── js
│ │ ├── jquery-3.2.1.min.js
│ │ └── md5.min.js
│ ├── templates
│ │ ├── admin
│ │ │ └── index.html
│ │ └── admin.html
│ └── utils.py
├── instance # 私密配置文件夾
│ ├── __init__.py
│ └── secure_conf.py
└── server.py # 主服務運行文件
項目運行
安裝Python2.7環境
安裝第三方依賴
pip install -r requirements.txt
運行
python server.py
運行后會在 src 目錄下生成 data.sqlite
文件,存儲數據庫相關數據,訪問 http://localhost:5000/admin/ 進入后台管理系統,管理系統賬號密碼保存在 src/instance/secure_conf.py
中,可自行更改。
項目實現
數據庫建模
電影系統的主要功能是用戶可以瀏覽電影信息並選擇場次和座位進行下單購票,完成支付后會隨機贈送優惠券,可在下次購票時使用。觀看完電影之后可進行電影評論,用戶也可以收藏電影,首頁會定期推薦新上映的熱門電影,因此該項目至少涉及以下幾張數據表:用戶表,電影表,場次表,訂單表,評論表,優惠券表,推薦表,收藏表。
完整的數據庫模型如下圖:
以上數據庫模型,以users為例,對應的sql語句如下:
-- ----------------------------
-- Table structure for users 用戶表
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` varchar(11) NOT NULL COMMENT '用戶id(手機號碼)',
`password` varchar(32) NOT NULL COMMENT '登錄密碼',
`payPassword` varchar(32) NOT NULL COMMENT '支付密碼',
`nickname` varchar(20) NOT NULL DEFAULT '' COMMENT '昵稱',
`money` float NOT NULL COMMENT '余額',
`description` varchar(50) NOT NULL DEFAULT '' COMMENT '個性簽名',
`avatar` varchar(32) NOT NULL DEFAULT '' COMMENT '頭像路徑',
`isAdmin` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
對象關系映射 ORM
對象關系映射(Object Relational Mapping,簡稱ORM),用於實現面向對象編程語言里不同類型系統的數據之間的轉換。從效果上說,它其實是創建了一個可在編程語言里使用的“虛擬對象數據庫”。對象關系映射(Object-Relational Mapping)提供了概念性的、易於理解的模型化數據的方法。在大多數的 Web 應用中,都會將數據存儲在關系型數據庫中,使用 ORM 技術可以在代碼通過操作對象實現數據庫操作,無需自己寫sql語句,如前面一大段建表的語句,ORM模型的簡單性簡化了數據庫查詢過程。使用ORM查詢工具,用戶可以訪問期望數據,而不必理解數據庫的底層結構。Flask 中可使用Flask-SQLAlchemy
來實現 ORM。
models.py
中定義了每張數據庫表對應的對象,通過對這些對象的操作,可以直接實現對數據庫的增刪改查,無需編寫sql語句。以訂單表為例:
# -*- coding: utf-8 -*-
from datetime import datetime, date
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Order(db.Model):
"""訂單"""
__tablename__ = 'orders'
__table_args__ = {'mysql_engine': 'InnoDB'} # 支持事務操作和外鍵
id = db.Column(db.String(32), primary_key=True)
screenId = db.Column(db.String(32), db.ForeignKey('screens.id'), nullable=False)
seat = db.Column(db.PickleType, doc='座位號(逗號分隔)', nullable=False)
username = db.Column(db.String(32), db.ForeignKey('users.id'), nullable=False)
createTime = db.Column(db.DateTime, doc='創建時間', nullable=False)
status = db.Column(db.Boolean, doc='訂單狀態(0:未支付,1:已支付)', default=0, nullable=False)
couponId = db.Column(db.String(32), db.ForeignKey('coupons.id'))
payPrice = db.Column(db.Float, doc='實際支付', nullable=False)
totalPrice = db.Column(db.Float, doc='原價', nullable=False)
管理系統
成功使用 ORM 建了項目所需的數據庫表,現在我們要考慮一個問題,我們要怎么管理數據庫中的數據呢,比如一個電影系統,肯定會有電影的信息和上映的場次信息,這個時候就需要一個后台管理系統,登錄該系統后可以對數據庫中的數據進行增刪改查。使用Flask-Admin
可以很方便的構建一個后台管理系統
初始化
from flask import Flask
from flask_admin import Admin
app = Flask(__name__)
admin = Admin(app, name=u'電影管理系統') # 等價於下面兩句
# admin = Admin(name=u'電影管理系統')
# admin.init_app(app)
app.run()
運行之后訪問 http://localhost:5000/admin/ 就可以看到一個簡單的管理界面。
增加視圖
作為后台管理系統,當然需要添加登錄界面,保證管理系統的安全性,用Flask-Login
做身份驗證,Flask-WTF
防止跨站請求偽造攻擊(CSRF)。
views.py
文件中,添加一個登錄表單的類,其中的 User 類就是數據模型中的用戶表,此時 User 類還需要繼承 flask_login 中的UserMixin類。
from models import User
from flask_wtf import FlaskForm
from wtforms import fields, validators, ValidationError
class LoginForm(FlaskForm):
"""登錄表單"""
username = fields.StringField(validators=[validators.data_required()])
password = fields.PasswordField(validators=[validators.data_required()])
def validate_username(self, field):
"""登錄校驗"""
user = self.get_user()
if user is None:
raise ValidationError('Invalid user')
if self.password.data != user.password:
raise ValidationError('Invalid password')
def get_user(self):
return User.query.filter_by(id=self.username.data, isAdmin=1).first()
有了登錄表單之后,增加一個視圖,如果未登錄就重定向到登錄界面,已登錄就顯示管理系統首頁,這里需要在templates文件夾中添加模板文件。
import flask_login as login
from flask_login import login_required
from flask_admin import expose, AdminIndexView, helpers
class MyAdminIndexView(AdminIndexView):
@expose('/')
def index(self):
if not isAdmin():
return redirect(url_for('.login_view'))
return super(MyAdminIndexView, self).index()
@expose('/login/', methods=('GET', 'POST'))
def login_view(self):
form = LoginForm(request.form)
if helpers.validate_form_on_submit(form):
user = form.get_user()
login.login_user(user)
if isAdmin():
return redirect(url_for('.index'))
self._template_args['form'] = form
return super(MyAdminIndexView, self).index()
@expose('/logout/')
@login_required
def logout_view(self):
login.logout_user()
return redirect(url_for('.login_view'))
數據庫模型視圖
為數據庫中的每個張表增加專用的管理頁面。比如現在要為電影單獨添加一個管理頁面,只需新建一個繼承ModelView的類
from wtforms import fields
from flask_admin.contrib.sqla import ModelView
class MovieModelView(ModelView):
column_exclude_list = ('description',) # 不顯示的字段
# 表單字段
form_columns = ('expired', 'name', 'poster', 'description',
'playingTime', 'duration', 'movieType', 'playingType')
form_create_rules = form_columns[1:] # 新建時顯示的字段
form_overrides = {'poster': fields.FileField} # 重寫表單類型
# 自定義字段顯示
form_args = {
'movieType': {
'render_kw': {
'placeholder': '電影類型, 中文逗號分隔'
}
}
}
# 當模型的數據改變時觸發(新建或修改)
def on_model_change(self, form, movie, is_created):
# do something
pass
然后初始化登錄,添加模型視圖即可
# -*- coding: utf-8 -*-
from views import *
import flask_login as login
from flask_admin import Admin
from models import db, Movie, User
def init_login(app):
"""初始化登錄"""
login_manager = login.LoginManager()
login_manager.init_app(app)
# Create user loader function
@login_manager.user_loader
def load_user(id):
return User.query.get(id)
admin = Admin(name='管理系統', template_mode='bootstrap3',
index_view=MyAdminIndexView(), base_template='admin.html')
movieModelView = MovieModelView(Movie, db.session, name='電影管理')
admin.add_view(movieModelView)
效果如下:
SwaggerUI
有了管理后台,就可以開始編寫后台接口了,Flask-RESTPlus
增加了對快速構建 REST API的支持。它配置非常簡單,很容易上手,而且它提供了裝飾器和工具集合來描述API 並且集成了 Swagger UI 界面。
在 REST 中,一個 URI 標識一個資源,Flask-RESTPlus 中有一個Resource類,繼承這個類並實現get,post,delete,patch等函數即可處理對應的 HTTP 請求。下面以收藏模塊為例:
# *-* coding: utf-8 *-*
from flask import request
from app.utils import UUID
from app.models import Favorite, Movie, db
from flask_restplus import Namespace, Resource
from flask_login import current_user, login_required
api = Namespace('favorite', description='收藏模塊')
@api.route('/')
class FavoritesResource(Resource):
@login_required
def get(self):
"""獲取收藏列表(需登錄)"""
return [f.__json__() for f in current_user.favorites], 200
@api.doc(parser=api.parser().add_argument(
'movieId', type=str, required=True, help='電影id', location='form')
)
@login_required
def post(self):
"""收藏電影(需登錄)"""
mid = request.form.get('movieId', '')
movie = Movie.query.get(mid)
if movie is None:
return {'message': '電影不存在'}, 233
movie = current_user.favorites.filter_by(movieId=mid).first()
if movie is not None:
return {'message': '不能重復收藏同部電影'}, 233
favorite = Favorite()
favorite.id = UUID()
favorite.username = current_user.id
favorite.movieId = mid
db.session.add(favorite)
db.session.commit()
return {'message': '收藏成功', 'id': favorite.id}, 200
@api.route('/<id>')
@api.doc(params={'id': '收藏id'})
class FavoriteResource(Resource):
@login_required
def delete(self, id):
"""取消收藏(需登錄)"""
favorite = current_user.favorites.filter_by(id=id).first()
if favorite is None:
return {'message': '您沒有這個收藏'}, 233
db.session.delete(favorite)
db.session.commit()
return {'message': '取消收藏成功'}, 200
@api.route
裝飾器是路由監聽,@login_required
裝飾器來自於 Flask-Login ,被裝飾的函數需要用戶登錄,@api.doc
是文檔說明,在后面的 Swagger UI 部分就能看到它的作用了。
創建了資源之后,只需要進行初始化即可實現 RESTful 服務。
# *-* coding: utf-8 *-*
from flask import Flask
from flask_restplus import Api
from favorite import api as ns1
api = Api(
title='MonkeyEye',
version='1.0',
description='電影系統API',
doc='/swagger/', # Swagger UI: http://localhost:5000/swagger/
catch_all_404s=True,
serve_challenge_on_401=True
)
api.add_namespace(ns1, path='/api/favorites')
app = Flask(__name__)
api.init_app(app)
if __name__ == '__main__':
app.run()
實現了 REST API 之后,我們要怎么測試這些接口呢,有沒有一種操作簡便的方法呢? Flask-RESTPlus 中就自帶了 Swagger UI 界面,可以直接訪問該界面查看 API 並進行測試,前面我們用@api.doc
修飾的資源和方法都會在 Swagger UI 界面呈現出來,Api(doc=/swagger/) 指定了 Swagger UI 的路徑,效果如下圖:
改進優化
為了例子能夠簡單快速運行,減少環境依賴,刪除了原項目很多的功能點:
- MySQL數據庫,例子中改用sqlite,可自行安裝其他數據庫,修改配置文件中的數據庫URL即可。
- 手機號注冊,短信驗證碼功能
- 使用QQ郵箱發送重置密碼右鍵功能
- gunicorn + Nginx 部署,提高程序性能
參考鏈接:
- Flask 文檔:https://dormousehole.readthedocs.io/en/latest/
- Flask-Admin 教程:http://flask123.sinaapp.com/article/57/
- Flask-RESTplus 教程:https://www.cnblogs.com/leejack/p/9162367.htmlPython-Flask實現電影系統管理后台