Python-Flask实现电影系统管理后台

代码地址以下:<br>http://www.demodashi.com/demo/14850.htmlhtml

项目描述

该项目实现电影系统的后台接口,包括用户,电影,场次,订单,评论,优惠券,推荐,收藏等多个模块,同时提供一个管理后台进行操做和管理。使用Swagger使用接口的可视化,方便测试。python

项目截图

项目结构

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 中,可自行更改。mysql

项目实现

数据库建模

电影系统的主要功能是用户能够浏览电影信息并选择场次和座位进行下单购票,完成支付后会随机赠送优惠券,可在下次购票时使用。观看完电影以后可进行电影评论,用户也能够收藏电影,首页会按期推荐新上映的热门电影,所以该项目至少涉及如下几张数据表:用户表,电影表,场次表,订单表,评论表,优惠券表,推荐表,收藏表。 完整的数据库模型以下图: 以上数据库模型,以users为例,对应的sql语句以下:jquery

-- ----------------------------
-- 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。sql

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/ 就能够看到一个简单的管理界面。json

增长视图

做为后台管理系统,固然须要添加登陆界面,保证管理系统的安全性,用Flask-Login作身份验证,Flask-WTF防止跨站请求伪造攻击(CSRF)。flask

views.py文件中,添加一个登陆表单的类,其中的 User 类就是数据模型中的用户表,此时 User 类还须要继承 flask_login 中的UserMixin类。bootstrap

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 部署,提升程序性能

参考连接:

代码地址以下:<br>http://www.demodashi.com/demo/14850.html

相关文章
相关标签/搜索