1. 程式人生 > >Flask 構建微電影視訊網站(四)

Flask 構建微電影視訊網站(四)

後臺管理

實現後臺管理系統使用flask sqlalchemy結合mysql資料庫進行增刪改查操作、分頁的使用、路由裝飾器定義、模板中變數呼叫、登入會話機制、上傳檔案、flask wtforms表單使用。

管理員登入

models進行重構,將資料庫的配置資訊放在app/__init__.py檔案中

from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://root:[email protected]
:3306/movie' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True app.config['SECRET_KEY'] = 'cb34xxxxxxxxxxxxxxxxxxbae30d90f6' db = SQLAlchemy(app)

models檔案中直接引入db

from app import db

定義登陸表單欄位
app/admin/forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, ValidationError

from app.models import Admin


class LoginForm(FlaskForm):
    '''
    管理員登陸表單
    '''
    account = StringField(
        label='賬號',
        validators=[
            DataRequired("請輸入賬號!")
        ],
        description="賬號",
        render_kw={
            "class": "form-control",
            "placeholder": "請輸入賬號!",
            "required": "required"
        }
    )
    pwd = PasswordField(
        label='密碼',
        validators=[
            DataRequired("請輸入密碼!")
        ],
        description="密碼",
        render_kw={
            "class": "form-control",
            "placeholder": "請輸入密碼!",
            "required": "required"
        }
    )
    submit = SubmitField(
        '登入',
        render_kw={
            "class": "btn btn-primary btn-block btn-flat",
        }
    )

    def validate_account(self, field):
        account = field.data
        admin = Admin.query.filter_by(name=account).count()
        if admin == 0:
            raise ValidationError("賬號不存在!")

render_kw裡的樣式是前端程式碼中的

編寫試圖函式

# 裝飾器用來進行訪問控制
def admin_login_req(func):
    @wraps(func)
    def decorated_function(*args, **kwargs):
        if session.get('admin', None) is None:
            return redirect(url_for('admin.login', next=request.url))
        return func(*args, **kwargs)
    return decorated_function
@admin.route('/login/', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        data = form.data
        admin = Admin.query.filter_by(name=data['account']).first()
        if not admin.check_pwd(data['pwd']):
            flash("賬號或密碼錯誤! ")
            return redirect(url_for('admin.login'))
        session['admin'] = data['account']
        return redirect((request.args.get('next') or url_for('admin.index')))
    return render_template('admin/login.html', form=form)


@admin.route('/logout/')
@admin_login_req
def logout():
    session.clear()
    return redirect(url_for('admin.login'))

在每一個需要進行登陸才能操作的檢視函式中加入裝飾器,像logout檢視一樣
Admin模型中新增密碼校驗函式

    def check_pwd(self, pwd):
        from werkzeug.security import check_password_hash
        return check_password_hash(self.pwd, pwd)

修改模板app/templates/admin/login.html

    <div class="login-box-body">
        {% for message in get_flashed_messages() %}
            <p class="login-box-msg" style="color: red">{{ message }}</p>
        {% endfor %}

        <form action="" method="post" id="form-data">
            <div class="form-group has-feedback">
                {{ form.account }}
                <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
                {% for err in form.account.errors %}
                    <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
                {% endfor %}

            </div>
            <div class="form-group has-feedback">
                {{ form.pwd }}
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
                {% for err in form.pwd.errors %}
                    <div class="col-md-12" id="input_pwd" style="color: red">{{ err }}</div>
                {% endfor %}
            </div>
            <div class="row">
                <div class="col-xs-8">
                </div>
                <div class="col-xs-4">
                    {{ form.submit }}
                    {{ form.csrf_token }}
                </div>
            </div>
        </form>
    </div>

標籤管理

建立一個表單form
app/admin/forms.py

class TagForm(FlaskForm):
    name = StringField(
        label='名稱',
        validators=[
            DataRequired("請輸入標籤!")
        ],
        description="名稱",
        render_kw={
            "class": "form-control",
            "id": "input_name",
            "placeholder": "請輸入標籤名稱!"
        }
    )
    submit = SubmitField(
        '編輯',
        render_kw={
            "class": "btn btn-primary"
        }
    )
新增標籤

檢視函式

@admin.route('/tag/add/', methods=['GET', 'POST'])
@admin_login_req
def tag_add():
    form = TagForm()
    if form.validate_on_submit():
        data = form.data
        tag = Tag.query.filter_by(name=data['name']).count()
        if tag == 1:
            flash("標籤已存在!", 'error')
            return redirect(url_for('admin.tag_add'))
        tag = Tag(
            name=data['name']
        )
        db.session.add(tag)
        db.session.commit()
        flash("標籤新增成功!", 'info')
        return redirect(url_for('admin.tag_add'))
    return render_template('admin/tag_add.html', form=form)

修改前端程式碼

<form role="form" method="post">
    <div class="box-body">
        {% for message in get_flashed_messages(category_filter=['info']) %}
            <div class="alert alert-success alert-dismissible">
                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                </button>
                <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                {{ message }}
            </div>
        {% endfor %}
        {% for message in get_flashed_messages(category_filter=['error']) %}
            <div class="alert alert-danger alert-dismissible">
                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                </button>
                <h4><i class="icon fa fa-ban"></i> 操作失敗!</h4>
                {{ message }}
            </div>
        {% endfor %}


        <div class="form-group">
            <label for="input_name">{{ form.name.label }}</label>
            {{ form.name }}
            {% for err in form.name.errors %}
                <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
    </div>
    <div class="box-footer">
        {{ form.submit }}
        {{ form.csrf_token }}
    </div>
</form>
標籤列表

檢視函式

@admin.route('/tag/list/<int:page>/')
@admin_login_req
def tag_list(page=1):
    if page <= 0:
        page = 1
    page_data = Tag.query.order_by(
        Tag.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/tag_list.html', page_data=page_data)

修改前端程式碼

<div class="box-body table-responsive no-padding">
    {% for message in get_flashed_messages(category_filter=['info']) %}
        <div class="alert alert-success alert-dismissible">
            <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
            </button>
            <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
            {{ message }}
        </div>
    {% endfor %}
    <table class="table table-hover">
        <tbody>
        <tr>
            <th>編號</th>
            <th>名稱</th>
            <th>新增時間</th>
            <th>操作事項</th>
        </tr>
        {% for tag in page_data.items %}
            <tr>
                <td>{{ tag.id }}</td>
                <td>{{ tag.name }}</td>
                <td>{{ tag.addtime }}</td>
                <td>
                    <a href="{{ url_for('admin.tag_edit', id=tag.id) }}" class="label label-success">編輯</a>
                    &nbsp;
                    <a href="{{ url_for('admin.tag_del', id=tag.id) }}" class="label label-danger">刪除</a>
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
</div>

當標籤較多時,需要對標籤進行分頁
分頁http://www.pythondoc.com/flask-sqlalchemy/api.html?highlight=paginate#id4

新建一個分頁的macro
app/templates/ui/admin_page.html

{% macro pagination(data, url) -%}
    {% if data %}
        <ul class="pagination pagination-sm no-margin pull-right">
            <li><a href="{{ url_for(url, page=1) }}">首頁</a></li>

            {% if data.has_prev %}
                <li><a href="{{ url_for(url, page=data.prev_num) }}">上一頁</a></li>
            {% else %}
                <li class="disabled"><a href="#">上一頁</a></li>
            {% endif %}

            {% for v in data.iter_pages() %}
                {% if v == data.page %}
                    <li class="active"><a href="#">{{ v }}</a></li>
                {% else %}
                    <li><a href="{{ url_for(url, page=v) }}">{{ v }}</a></li>
                {% endif %}
            {% endfor %}

            {% if data.has_next %}
                <li><a href="{{ url_for(url, page=data.next_num) }}">下一頁</a></li>
            {% else %}
                <li class="disabled"><a href="#">下一頁</a></li>
            {% endif %}

            <li><a href="{{ url_for(url, page=data.pages) }}">尾頁</a></li>
        </ul>

    {% endif %}
{%- endmacro %}

在標籤列表中使用這個macro

{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}

...

<div class="box-footer clearfix">
    {{ pagination(page_data, 'admin.tag_list') }}
</div>
刪除標籤

檢視函式

@admin.route('/tag/del/<int:id>/')
@admin_login_req
def tag_del(id=None):
    tag = Tag.query.filter_by(id=id).first_or_404()
    db.session.delete(tag)
    db.session.commit()
    flash('刪除標籤成功!', 'info')
    return redirect(url_for('admin.tag_list', page=1))

修改標籤列表中刪除按鈕的a標籤

<a href="{{ url_for('admin.tag_del', id=tag.id) }}" class="label label-danger">刪除</a>
修改標籤

檢視函式

@admin.route('/tag/edit/<int:id>/', methods=['GET', 'POST'])
@admin_login_req
def tag_edit(id=None):
    form = TagForm()
    tag = Tag.query.get_or_404(id)
    if form.validate_on_submit():
        data = form.data
        tag_count = Tag.query.filter_by(name=data['name']).count()
        if tag.name != data['name'] and tag_count == 1:
            flash("標籤已存在!", 'error')
            return redirect(url_for('admin.tag_edit', id=id))
        tag.name=data['name']
        db.session.add(tag)
        db.session.commit()
        flash("標籤修改成功!", 'info')
        return redirect(url_for('admin.tag_list', page=1))
    return render_template('admin/tag_edit.html', form=form, tag=tag)

新建app/templates/admin/tag_edit.html,用來進行標籤的修改
程式碼和新增標籤中的幾乎一致,只是需要顯示標籤的名字

<div class="form-group">
    <label for="input_name">{{ form.name.label }}</label>
    {{ form.name(value=tag.name) }}
    {% for err in form.name.errors %}
        <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
    {% endfor %}
</div>

電影管理

新建電影表單
app/admin/forms.py

class MovieForm(FlaskForm):
    title = StringField(
        label='片名',
        validators=[
            DataRequired("請輸入片名!")
        ],
        description="片名",
        render_kw={
            "class": "form-control",
            "id": "input_title",
            "placeholder": "請輸入片名!"
        }
    )
    url = FileField(
        label='檔案',
        validators=[
            DataRequired("請上傳檔案!")
        ],
        description="檔案",
    )
    info = TextAreaField(
        label='簡介',
        validators=[
            DataRequired("請輸入簡介!")
        ],
        description="簡介",
        render_kw={
            "class": "form-control",
            "rows": "10",
            "id": "input_info",
        }
    )

    logo = FileField(
        label='封面',
        validators=[
            DataRequired("請上傳封面!")
        ],
        description="封面",
    )
    star = SelectField(
        label='星級',
        validators=[
            DataRequired("請選擇星級!")
        ],
        coerce=int,
        choices=[(1, '1星'), (2, '2星'), (3, '3星'), (4, '4星'), (5, '5星')],
        description="星級",
        render_kw={
            "class": "form-control",
        }
    )
    tag_id = SelectField(
        label='標籤',
        validators=[
            DataRequired("請選擇標籤!")
        ],
        coerce=int,
        choices=[(v.id, v.name) for v in tags],
        description="標籤",
        render_kw={
            "class": "form-control",
        }
    )
    area = StringField(
        label='地區',
        validators=[
            DataRequired("請輸入地區!")
        ],
        description="地區",
        render_kw={
            "class": "form-control",
            "placeholder": "請輸入地區!"
        }
    )
    length = StringField(
        label='片長',
        validators=[
            DataRequired("請輸入片長!")
        ],
        description="片長",
        render_kw={
            "class": "form-control",
            "placeholder": "請輸入片長!"
        }
    )
    release_time = StringField(
        label='上映時間',
        validators=[
            DataRequired("請選擇上映時間!")
        ],
        description="上映時間",
        render_kw={
            "class": "form-control",
            "id": "input_release_time",
            "placeholder": "請選擇上映時間!"
        }
    )
    submit = SubmitField(
        '編輯',
        render_kw={
            "class": "btn btn-primary"
        }
    )

    def validate_title(self, field):
        title = field.data
        num = Movie.query.filter_by(title=title).count()
        if num > 0:
            raise ValidationError("該電影已存在!")

在app初始化檔案中定義上傳檔案的目錄

app.config['UP_DIR'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static/uploads/')
新增電影

檢視函式
檔名稱檢測

from werkzeug.utils import secure_filename

def change_filename(filename):
    fileinfo = os.path.splitext(filename)
    filename = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + str(uuid.uuid4().hex) + fileinfo[-1]
    return filename
@admin.route('/movie/add/', methods=['GET', 'POST'])
@admin_login_req
def movie_add():
    form = MovieForm()
    if form.validate_on_submit():
        data = form.data
        file_url = secure_filename(form.url.data.filename)
        file_logo = secure_filename(form.logo.data.filename)

        if not os.path.exists(app.config['UP_DIR']):
            os.makedirs(app.config['UP_DIR'])
            os.chmod(app.config['UP_DIR'], 6)

        url = change_filename(file_url)
        logo = change_filename(file_logo)
        form.url.data.save(app.config['UP_DIR'] + url)
        form.logo.data.save(app.config['UP_DIR'] + logo)

        movie = Movie(
            title=data['title'],
            url=url,
            info=data['info'],
            logo=logo,
            star=int(data['star']),
            playnum=0,
            commentnum=0,
            tag_id=int(data['tag_id']),
            area=data['area'],
            release_time=data['release_time'],
            length=data['length']
        )
        db.session.add(movie)
        db.session.commit()
        flash('電影新增成功!', 'info')
        return redirect(url_for('admin.movie_add'))
    return render_template('admin/movie_add.html', form=form)

app/templates/admin/movie_add.html

{% extends 'admin/admin.html' %}

{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 電影管理</a></li>
            <li class="active">新增電影</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <h3 class="box-title">新增電影</h3>
                    </div>
                    <form role="form" method="post" enctype="multipart/form-data">
                        <div class="box-body">
                            {% for message in get_flashed_messages(category_filter=['info']) %}
                                <div class="alert alert-success alert-dismissible">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                    </button>
                                    <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                    {{ message }}
                                </div>
                            {% endfor %}
                            <div class="form-group">
                                <label for="input_title">{{ form.title.label }}</label>
                                {{ form.title }}
                                {% for err in form.title.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_url">{{ form.url.label }}</label>
                                {{ form.url }}
                                {% for err in form.url.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                                <div style="margin-top:5px;">
{#                                    <div id="moviecontainer"></div>#}
                                </div>
                            </div>
                            <div class="form-group">
                                <label for="input_info">{{ form.info.label }}</label>
                                {{ form.info }}
                                {% for err in form.info.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_logo">{{ form.logo.label }}</label>
                                {{ form.logo }}
                                {% for err in form.logo.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
{#                                <img data-src="holder.js/262x166" style="margin-top:5px;" class="img-responsive"#}
{#                                     alt="">#}
                            </div>
                            <div class="form-group">
                                <label for="input_star">{{ form.star.label }}</label>
                                {{ form.star }}
                                {% for err in form.star.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_tag_id">{{ form.tag_id.label }}</label>
                                {{ form.tag_id }}
                                {% for err in form.tag_id.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_area">{{ form.area.label }}</label>
                                {{ form.area }}
                                {% for err in form.area.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_length">{{ form.length.label }}</label>
                                {{ form.length }}
                                {% for err in form.length.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_release_time">{{ form.release_time.label }}</label>
                                {{ form.release_time }}
                                {% for err in form.release_time.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                        </div>
                        <div class="box-footer">
                            {{ form.csrf_token }}
                            {{ form.submit }}
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>
{% endblock %}
電影列表

檢視函式

@admin.route('/movie/list/<int:page>/')
@admin_login_req
def movie_list(page=1):
    if page <= 0:
        page = 1
    page_data = Movie.query.join(Tag).filter(
        Tag.id == Movie.tag_id
    ).order_by(
        Movie.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/movie_list.html', page_data=page_data)

app/templates/admin/movie_list.html

{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}
{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 電影管理</a></li>
            <li class="active">電影列表</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header">
                        <h3 class="box-title">電影列表</h3>
                        <div class="box-tools">
                            <div class="input-group input-group-sm" style="width: 150px;">
                                <input type="text" name="table_search" class="form-control pull-right"
                                       placeholder="請輸入關鍵字...">

                                <div class="input-group-btn">
                                    <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="box-body table-responsive no-padding">
                        {% for message in get_flashed_messages(category_filter=['info']) %}
                            <div class="alert alert-success alert-dismissible">
                                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                </button>
                                <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                {{ message }}
                            </div>
                        {% endfor %}
                        <table class="table table-hover">
                            <tbody>
                            <tr>
                                <th>編號</th>
                                <th>片名</th>
                                <th>片長</th>
                                <th>標籤</th>
                                <th>地區</th>
                                <th>星級</th>
                                <th>播放數量</th>
                                <th>評論數量</th>
                                <th>上映時間</th>
                                <th>操作事項</th>
                            </tr>
                            {% for data in page_data.items %}

                                <tr>
                                    <td>{{ data.id }}</td>
                                    <td>{{ data.title }}</td>
                                    <td>{{ data.length }}分鐘</td>
                                    <td>{{ data.tag.name }}</td>
                                    <td>{{ data.area }}</td>
                                    <td>{{ data.star }}</td>
                                    <td>{{ data.playnum }}</td>
                                    <td>{{ data.commentnum }}</td>
                                    <td>{{ data.release_time }}</td>
                                    <td>
                                        <a href="{{ url_for('admin.movie_edit', id=data.id) }}" class="label label-success">編輯</a>
                                        &nbsp;
                                        <a href="{{ url_for('admin.movie_del', id=data.id) }}"
                                           class="label label-danger">刪除</a>
                                    </td>
                                </tr>
                            {% endfor %}

                            </tbody>
                        </table>
                    </div>
                    <div class="box-footer clearfix">
                        {{ pagination(page_data, 'admin.movie_list') }}
                    </div>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block js %}
    <script>
        $(document).ready(function () {
            $('#g-3').addClass('active');
            $('#g-3-2').addClass('active');
        })
    </script>
{% endblock %}

修改app/templates/admin/grid.html

<li id="g-3-2">
    <a href="{{ url_for('admin.movie_list', page=1) }}">
        <i class="fa fa-circle-o"></i> 電影列表
    </a>
</li>
刪除電影

檢視函式

@admin.route('/movie/del/<int:id>/')
@admin_login_req
def movie_del(id=None):
    movie = Movie.query.get_or_404(int(id))
    db.session.delete(movie)
    db.session.commit()
    flash('電影刪除成功!', 'info')
    return redirect(url_for('admin.movie_list', page=1))

修改一下前端刪除按鈕a標籤

修改電影

檢視函式

@admin.route('/movie/edit/<int:id>', methods=['GET', 'POST'])
@admin_login_req
def movie_edit(id=None):
    form = MovieForm()

    # # 如果不設定,預設依然會讓上傳檔案
    # form.url.flags.required = False
    # form.logo.flags.required = False

    # # 取消校驗,可能沒有上傳檔案
    # form.url.validators=[]
    # form.logo.validators=[]
    # 取消後如果沒有上傳檔案,form.url.data是一個str物件
    # 上傳檔案後是才是一個檔案物件
    # 為了方便,設定必須上傳檔案

    movie=Movie.query.get_or_404(int(id))
    if  request.method =='GET':
        form.info.data = movie.info
        form.tag_id.data = movie.tag_id
        form.star.data = movie.star
    if form.validate_on_submit():
        data = form.data
        movie_count = Movie.query.filter_by(title=data['title']).count()
        if movie_count == 1 and movie.title != data['title']:
            flash('該電影已存在!', 'error')
            return redirect(url_for('admin.movie_edit', id=id))

        if not os.path.exists(app.config['UP_DIR']):
            os.makedirs(app.config['UP_DIR'])
            os.chmod(app.config['UP_DIR'], 6)

        if form.url.data.filename !='':
            file_url = secure_filename(form.url.data.filename)
            movie.url = change_filename(file_url)
            form.url.data.save(app.config['UP_DIR'] + movie.url)

        if form.logo.data.filename != '':
            file_logo = secure_filename(form.logo.data.filename)
            movie.logo = change_filename(file_logo)
            form.logo.data.save(app.config['UP_DIR'] + movie.logo)

        movie.star = data['star']
        movie.tag_id = data['tag_id']
        movie.info = data['info']
        movie.title = data['title']
        movie.area = data['area']
        movie.length = data['length']
        movie.release_time = data['release_time']

        db.session.add(movie)
        db.session.commit()
        flash('電影修改成功!', 'info')
        return redirect(url_for('admin.movie_add', id=movie.id))
    return render_template('admin/movie_edit.html', form=form, movie=movie)

app/templates/admin/movie_edit.html

{% extends 'admin/admin.html' %}

{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 電影管理</a></li>
            <li class="active">修改電影</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <h3 class="box-title">修改電影</h3>
                    </div>
                    <form role="form" method="post" enctype="multipart/form-data">
                        <div class="box-body">
                            {% for message in get_flashed_messages(category_filter=['info']) %}
                                <div class="alert alert-success alert-dismissible">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                    </button>
                                    <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                    {{ message }}
                                </div>
                            {% endfor %}
                            {% for message in get_flashed_messages(category_filter=['error']) %}
                                <div class="alert alert-danger alert-dismissible">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                    </button>
                                    <h4><i class="icon fa fa-ban"></i> 操作失敗!</h4>
                                    {{ message }}
                                </div>
                            {% endfor %}
                            <div class="form-group">
                                <label for="input_title">{{ form.title.label }}</label>
                                {{ form.title(value=movie.title) }}
                                {% for err in form.title.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_url">{{ form.url.label }}</label>
                                {{ form.url }}
                                {% for err in form.url.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                                <div style="margin-top:5px;">
                                    <div id="moviecontainer"></div>
                                </div>
                            </div>
                            <div class="form-group">
                                <label for="input_info">{{ form.info.label }}</label>
                                {{ form.info }}
                                {% for err in form.info.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_logo">{{ form.logo.label }}</label>
                                {{ form.logo }}
                                {% for err in form.logo.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                                <img src="{{ url_for('static', filename='uploads/'+movie.logo) }}" style="margin-top:5px;" class="img-responsive"
                                     alt="">
                            </div>
                            <div class="form-group">
                                <label for="input_star">{{ form.star.label }}</label>
                                {{ form.star }}
                                {% for err in form.star.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_tag_id">{{ form.tag_id.label }}</label>
                                {{ form.tag_id }}
                                {% for err in form.tag_id.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_area">{{ form.area.label }}</label>
                                {{ form.area(value=movie.area) }}
                                {% for err in form.area.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_length">{{ form.length.label }}</label>
                                {{ form.length(value=movie.length) }}
                                {% for err in form.length.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_release_time">{{ form.release_time.label }}</label>
                                {{ form.release_time(value=movie.release_time) }}
                                {% for err in form.release_time.errors %}
                                    <div class="col-md-12" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                        </div>
                        <div class="box-footer">
                            {{ form.csrf_token }}
                            {{ form.submit }}
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block js %}
    <script src="{{ url_for('static',filename='jwplayer/jwplayer.js') }}"></script>
    <script type="text/javascript">
        jwplayer.key = "P9VTqT/X6TSP4gi/hy1wy23BivBhjdzVjMeOaQ==";

    </script>
    <script type="text/javascript">
        jwplayer("moviecontainer").setup({
            flashplayer: "{{ url_for('static',filename='jwplayer/jwplayer.flash.swf') }}",
            playlist: [{
                file: "{{ url_for('static', filename='uploads/'+movie.url) }}",
                title: "{{ movie.title }}"
            }],
            modes: [{
                type: "html5"
            }, {
                type: "flash",
                src: "{{ url_for('static',filename='jwplayer/jwplayer.flash.swf') }}"
            }, {
                type: "download"
            }],
            skin: {
                name: "vapor"
            },
            "playlist.position": "left",
            "playlist.size": 200,
            height: 250,
            width: 387,
        });

    </script>
    <script>
        $(document).ready(function () {
            $('#input_release_time').datepicker({
                autoclose: true,
                format: 'yyyy-mm-dd',
                language: 'zh-CN',
            });
        });

    </script>
    <script>
        $(document).ready(function () {
            $('#g-3').addClass('active');
            $('#g-3-1').addClass('active');
        })
    </script>
{% endblock %}

修改電影列表中的編輯按鈕

<a href="{{ url_for('admin.movie_edit', id=data.id) }}" class="label label-success">編輯</a>

預告管理

建立預告表單

class PreviewForm(FlaskForm):
    title = StringField(
        label='預告標題',
        validators=[
            DataRequired("請輸入預告標題!")
        ],
        description="預告標題",
        render_kw={
            "class": "form-control",
            "placeholder": "請輸入預告標題!"
        }
    )
    logo = FileField(
        label='預告封面',
        validators=[
            DataRequired("請上傳預告封面!"),
        ],
        description="預告封面",
    )
    submit = SubmitField(
        '編輯',
        render_kw={
            "class": "btn btn-primary"
        }
    )

    def validate_title(self, field):
        title = field.data
        num = Preview.query.filter_by(title=title).count()
        if num > 0:
            raise ValidationError("該預告已存在!")
新增預告

檢視函式

@admin.route('/preview/add/', methods=['GET', 'POST'])
@admin_login_req
def preview_add():
    form = PreviewForm()
    if form.validate_on_submit():
        data = form.data
        file_logo = secure_filename(form.logo.data.filename)

        if not os.path.exists(app.config['UP_DIR']):
            os.makedirs(app.config['UP_DIR'])
            os.chmod(app.config['UP_DIR'], 6)

        logo = change_filename(file_logo)
        form.logo.data.save(app.config['UP_DIR']+logo)
        preview = Preview(
            title=data['title'],
            logo=logo
        )
        db.session.add(preview)
        db.session.commit()
        flash("預告新增成功!", 'info')
        return redirect(url_for('admin.preview_add'))
    return render_template('admin/preview_add.html', form = form)

app/templates/admin/preview_add.html

{% extends 'admin/admin.html' %}

{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 預告管理</a></li>
            <li class="active">新增預告</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <h3 class="box-title">新增預告</h3>
                    </div>
                    <form role="form" method="post" enctype="multipart/form-data">
                        <div class="box-body">
                            {% for message in get_flashed_messages(category_filter=['info']) %}
                                <div class="alert alert-success alert-dismissible">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                    </button>
                                    <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                    {{ message }}
                                </div>
                            {% endfor %}
                            {% for message in get_flashed_messages(category_filter=['error']) %}
                                <div class="alert alert-danger alert-dismissible">
                                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                    </button>
                                    <h4><i class="icon fa fa-ban"></i> 操作失敗!</h4>
                                    {{ message }}
                                </div>
                            {% endfor %}
                            <div class="form-group">
                                <label for="input_title">{{ form.title.label }}</label>
                                {{ form.title }}
                                {% for err in form.title.errors %}
                                    <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
                                {% endfor %}
                            </div>
                            <div class="form-group">
                                <label for="input_logo">{{ form.logo.label }}</label>
                                {{ form.logo }}
                                {% for err in form.logo.errors %}
                                    <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
                                {% endfor %}
                                <img data-src="holder.js/700x320" style="margin-top:5px;" class="img-responsive"
                                     alt="">
                            </div>
                        </div>
                        <div class="box-footer">
                            {{ form.csrf_token }}
                            {{ form.submit }}
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block js %}
    <script>
        $(document).ready(function () {
            $('#g-4').addClass('active');
            $('#g-4-1').addClass('active');
        })
    </script>
{% endblock %}
預告列表

檢視函式

@admin.route('/preview/list/<int:page>/')
@admin_login_req
def preview_list(page=1):
    if page <= 0:
        page = 1
    page_data = Preview.query.order_by(
        Preview.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/preview_list.html', page_data=page_data)

app/templates/admin/preview_list.html

{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}
{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 預告管理</a></li>
            <li class="active">預告列表</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header">
                        <h3 class="box-title">預告列表</h3>
                        <div class="box-tools">
                            <div class="input-group input-group-sm" style="width: 150px;">
                                <input type="text" name="table_search" class="form-control pull-right"
                                       placeholder="請輸入關鍵字...">

                                <div class="input-group-btn">
                                    <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="box-body table-responsive no-padding">
                        {% for message in get_flashed_messages(category_filter=['info']) %}
                            <div class="alert alert-success alert-dismissible">
                                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                </button>
                                <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                {{ message }}
                            </div>
                        {% endfor %}
                        <table class="table table-hover">
                            <tbody>
                            <tr>
                                <th>編號</th>
                                <th>預告標題</th>
                                <th>預告封面</th>
                                <th>新增時間</th>
                                <th>操作事項</th>
                            </tr>
                            {% for data in page_data.items %}
                                <tr>
                                    <td>{{ data.id }}</td>
                                    <td>{{ data.title }}</td>
                                    <td>
                                        <img src="{{ url_for('static', filename='uploads/'+data.logo) }}"
                                             class="img-responsive center-block" alt="" style="width: 140px">
                                    </td>
                                    <td>{{ data.addtime }}</td>
                                    <td>
                                        <a href="{{ url_for('admin.preview_edit', id=data.id) }}" class="label label-success">編輯</a>
                                        &nbsp;
                                        <a href="{{ url_for('admin.preview_del', id=data.id) }}"
                                           class="label label-danger">刪除</a>
                                    </td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>
                    <div class="box-footer clearfix">
                        {{ pagination(page_data, 'admin.preview_list') }}
                    </div>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block js %}
    <script>
        $(document).ready(function () {
            $('#g-4').addClass('active');
            $('#g-4-2').addClass('active');
        })
    </script>
{% endblock %}

修改app/templates/admin/grid.html

<li id="g-4-2">
    <a href="{{ url_for('admin.preview_list', page=1) }}">
        <i class="fa fa-circle-o"></i> 預告列表
    </a>
</li>
刪除預告

檢視函式

@admin.route('/preview/del/<int:id>/')
@admin_login_req
def preview_del(id=None):
    preview = Preview.query.get_or_404(int(id))
    db.session.delete(preview)
    db.session.commit()
    flash('預告刪除成功!', 'info')
    return redirect(url_for('admin.preview_list', page=1))
修改預告

檢視函式

@admin.route('/preview/edit/<int:id>/', methods=['GET', 'POST'])
@admin_login_req
def preview_edit(id=None):
    form = PreviewForm()
    preview = Preview.query.get_or_404(int(id))

    if request.method == 'GET':
        form.title.data = preview.title
    if form.validate_on_submit():
        data = form.data

        if not os.path.exists(app.config['UP_DIR']):
            os.makedirs(app.config['UP_DIR'])
            os.chmod(app.config['UP_DIR'], 6)

        if form.logo.data.filename != '':
            file_logo = secure_filename(form.logo.data.filename)
            preview.logo = change_filename(file_logo)
            form.logo.data.save(app.config['UP_DIR'] + preview.logo)

        preview.title=data['title']
        db.session.add(preview)
        db.session.commit()

        flash('預告修改成功!', 'info')
        return redirect(url_for('admin.preview_edit', id=id))
    return render_template('admin/preview_edit.html', form=form, preview=preview)

app/templates/admin/preview_edit.html,程式碼拷貝新增預告,修改部分

<div class="form-group">
    <label for="input_title">{{ form.title.label }}</label>
    {{ form.title(value=preview.title) }}
    {% for err in form.title.errors %}
        <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
    {% endfor %}
</div>
<div class="form-group">
    <label for="input_logo">{{ form.logo.label }}</label>
    {{ form.logo }}
    {% for err in form.logo.errors %}
        <div class="col-md-12" id="input_user" style="color: red">{{ err }}</div>
    {% endfor %}
    <img src="{{ url_for('static', filename='uploads/'+preview.logo) }}" style="margin-top:5px;" class="img-responsive"
         alt="">
</div>

會員管理

會員列表
@admin.route('/user/list/<int:page>/')
@admin_login_req
def user_list(page=1):
    if page <= 0:
        page = 1
    page_data = User.query.order_by(
        User.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/user_list.html', page_data=page_data)

app/templates/admin/user_list.html

{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}

{% block content %}
    <section class="content-header">
        <h1>微電影管理系統</h1>
        <ol class="breadcrumb">
            <li><a href="#"><i class="fa fa-dashboard"></i> 會員管理</a></li>
            <li class="active">會員列表</li>
        </ol>
    </section>
    <section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header">
                        <h3 class="box-title">會員列表</h3>
                        <div class="box-tools">
                            <div class="input-group input-group-sm" style="width: 150px;">
                                <input type="text" name="table_search" class="form-control pull-right"
                                       placeholder="請輸入關鍵字...">

                                <div class="input-group-btn">
                                    <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="box-body table-responsive no-padding">
                        {% for message in get_flashed_messages(category_filter=['info']) %}
                            <div class="alert alert-success alert-dismissible">
                                <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
                                </button>
                                <h4><i class="icon fa fa-check"></i> 操作成功!</h4>
                                {{ message }}
                            </div>
                        {% endfor %}
                        <table class="table table-hover">
                            <tbody>
                            <tr>
                                <th>編號</th>
                                <th>暱稱</th>
                                <th>郵箱</th>
                                <th>手機</th>
                                <th>頭像</th>
                                {#                                    <th>狀態</th>#}
                                <th>註冊時間</th>
                                <th>操作事項</th>
                            </tr>
                            {% for data in page_data.items %}



                                <tr>
                                    <td>{{ data.id }}</td>
                                    <td>{{ data.name }}</td>
                                    <td>{{ data.email }}</td>
                                    <td>{{ data.phone }}</td>
                                    <td>
                                        <img src="{{ url_for('static', filename='uploads/users/'+data.face) }}"
                                             style="width: 50px" class="img-responsive center-block" alt="">
                                    </td>
                                    {#                                    <td>正常/凍結</td>#}
                                    <td>{{ data.addtime }}</td>
                                    <td>
                                        <a class="label label-success"
                                           href="{{ url_for('admin.user_view', id=data.id) }}">檢視</a>
                                        {#                                        &nbsp;#}
                                        {#                                        <a class="label label-info">解凍</a>#}
                                        {#                                        &nbsp;#}
                                        {#                                        <a class="label label-warning">凍結</a>#}
                                        &nbsp;
                                        <a href="{{ url_for('admin.user_del', id=data.id) }}"
                                           class="label label-danger">刪除</a>
                                    </td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>
                    <div class="box-footer clearfix">
                        {{ pagination(page_data, 'admin.preview_list') }}
                    </div>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block js %}
    <script>
        $(document).ready(function () {
            $('#g-5').addClass('active');
            $('#g-5-1').addClass('active');
        })
    </script>
{% endblock %}
檢視會員
@admin.route('/user/view/<int:id>/')
@admin_login_req
def user_view(id=None):
    user = User.query.get_or_404(int(id))
    return render_template('admin/user_view.html', user=user)
<table class="table table-hover">
    <tbody>
    <tr>
        <td class="td_bd">編號:</td>
        <td>1</td>
    </tr>
    <tr>
        <td class="td_bd">暱稱:</td>
        <td>{{ user.name }}</td>
    </tr>
    <tr>
        <td class="td_bd">郵箱:</td>
        <td>{{ user.email }}</td>
    </tr>
    <tr>
        <td class="td_bd">手機:</td>
        <td>{{ user.phone }}</td>
    </tr>
    <tr>
        <td class="td_bd">頭像:</td>
        <td>
            <img src="{{ url_for('static', filename='uploads/users/'+user.face) }}" style="width: 100px" class="img-responsive" alt="">
        </td>
    </tr>
    <tr>
        <td class="td_bd">註冊時間:</td>
        <td>
            {{ user.addtime }}
        </td>
    </tr>
    <tr>
        <td class="td_bd">唯一標誌符:</td>
        <td>
            {{ user.uuid }}
        </td>
    </tr>
    <tr>
        <td class="td_bd">個性簡介:</td>
        <td>
            {{ user.info }}
        </td>
    </tr>
    </tbody>
</table>
刪除使用者
@admin.route('/user/del/<int:id>/')
@admin_login_req
def user_del(id=None):
    user = User.query.get_or_404(int(id))
    db.session.delete(user)
    db.session.commit()
    flash('會員刪除成功!', 'info')
    return redirect(url_for('admin.user_list', page=1))

評論管理

評論列表
@admin.route('/comment/list/<int:page>/')
@admin_login_req
def comment_list(page=1):
    if page <= 0:
        page = 1
    page_data = Comment.query.join(
        Movie
    ).join(
        User
    ).filter(
        Movie.id==Comment.movie_id,
        User.id ==Comment.user_id
    ).order_by(
        Comment.addtime.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/comment_list.html', page_data=page_data)
<section class="content" id="showcontent">
        <div class="row">
            <div class="col-md-12">
                <div class="box box-primary">
                    <div class="box-header with-border">
                        <h3 class="box-title">評論列表</h3>
                        <div class="box-tools">
                            <div class="input-group input-group-sm" style="width: 150px;">
                                <input type="text" name="table_search" class="form-control pull-right"
                                       placeholder="請輸入關鍵字...">

                                <div class="input-group-btn">
                                    <button type="submit" class="btn btn-default"><i class="fa fa-search"></i>
                                    </button>
                                </div>
                            </div>
                        </div>
                    </div>