02: flask 使用举例

1.1 项目说明

    https://github.com/rickyyangrui/Flask_web_demo1

  1、项目文件结构

      

  2、项目主文件

cssmin==0.2.0
Flask==0.11.1
Flask-Admin==1.4.2
Flask-Assets==0.12
Flask-Avatar==0.1.0
Flask-BabelEx==0.9.3
Flask-Bootstrap==3.3.7.0
Flask-Cache==0.13.1
Flask-Login==0.3.2
Flask-Mail==0.9.1
Flask-Maple==0.4.7
Flask-Migrate==1.8.0
flask-msearch==0.1.2
Flask-Principal==0.4.0
Flask-Script==2.0.5
Flask-SQLAlchemy==2.2
Flask-WTF==0.14.2
gevent==1.1.1
greenlet==0.4.12
gunicorn==19.4.5
html5lib==0.9999999
itsdangerous==0.24
Jinja2==2.8
Mako==1.0.7
MarkupSafe==1.0
MySQL-python==1.2.5
Pillow==3.2.0
psycopg2==2.7.3.1
PyMySQL==0.7.11
python-dateutil==2.6.1
python-editor==1.0.3
pytz==2017.2
six==1.10.0
speaklater==1.3
SQLAlchemy==1.1.14
visitor==0.1.3
webassets==0.12.1
Werkzeug==0.12.2
Whoosh==2.7.4
WTForms==2.1
requirements.txt
#encoding: utf-8

from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from platform import app
from exts import db
from models import User, Question, Answer

manager = Manager(app)

# 使用Migrate绑定app和db
migrate = Migrate(app, db)

# 添加迁移脚本的命令到manager中
manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()
manage.py
#encoding: utf-8
import os

DEBUG = True

SECRET_KEY = os.urandom(24)

SQLALCHEMY_DATABASE_URI = 'mysql://root:1@localhost/testdb'
SQLALCHEMY_TRACK_MODIFICATIONS = True
config.py
#encoding: utf-8

from flask import Flask, render_template, request, redirect, url_for, session
import config
from models import User, Question, Answer
from exts import db
from decorators import login_required
from sqlalchemy import or_

app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)


@app.route('/')
def index():
    context = {
        'questions': Question.query.order_by('-create_time').all()
    }
    return render_template('index.html', **context)


@app.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        telephone = request.form.get('telephone')
        password = request.form.get('password')
        user = User.query.filter(User.telephone == telephone, User.password ==
                                 password).first()
        if user:
            session['user_id'] = user.id
            # 如果想在31天内都不需要登录
            session.permanent = True
            return redirect(url_for('index'))
        else:
            return u'手机号码或者密码错误,请确认好在登录'

@app.route('/regist/', methods=['GET', 'POST'])
def regist():
    if request.method == 'GET':
        return render_template('regist.html')
    else:
        telephone = request.form.get('telephone')
        username = request.form.get('username')
        password1 = request.form.get('password1')
        password2 = request.form.get('password2')

        # 手机号码验证,如果被注册了就不能用了
        user = User.query.filter(User.telephone == telephone).first()
        if user:
            return u'该手机号码被注册,请更换手机'
        else:
            # password1 要和password2相等才可以
            if password1 != password2:
                return u'两次密码不相等,请核实后再填写'
            else:
                user = User(telephone=telephone, username=username, password=password1)
                db.session.add(user)
                db.session.commit()
                # 如果注册成功,就让页面跳转到登录的页面
                return redirect(url_for('login'))

# 判断用户是否登录,只要我们从session中拿到数据就好了   注销函数
@app.route('/logout/')
def logout():
    # session.pop('user_id')
    # del session('user_id')
    session.clear()
    return redirect(url_for('login'))

@app.route('/question/', methods=['GET', 'POST'])
@login_required
def question():
    if request.method == 'GET':
        return render_template('question.html')
    else:
        title = request.form.get('title')
        content = request.form.get('content')
        question = Question(title=title, content=content)
        user_id = session.get('user_id')
        user = User.query.filter(User.id == user_id).first()
        question.author = user
        db.session.add(question)
        db.session.commit()
        return redirect(url_for('index'))

@app.route('/detail/<question_id>/')
def detail(question_id):
    question_model = Question.query.filter(Question.id == question_id).first()
    return render_template('detail.html', question=question_model)

@app.route('/add_answer/', methods=['POST'])
@login_required
def add_answer():
    content = request.form.get('answer_content')
    question_id = request.form.get('question_id')
    answer = Answer(content=content)
    user_id = session['user_id']
    user = User.query.filter(User.id == user_id).first()
    answer.author = user
    question = Question.query.filter(Question.id == question_id).first()
    answer.question = question
    db.session.add(answer)
    db.session.commit()
    return redirect(url_for('detail', question_id=question_id))

@app.route('/search/')
def search():
    q = request.args.get('q')
    # title, content
    # 或 查找方式(通过标题和内容来查找)
    # questions = Question.query.filter(or_(Question.title.contains(q),
    #                                     Question.content.constraints(q))).order_by('-create_time')
    # 与 查找(只能通过标题来查找)
    questions = Question.query.filter(Question.title.contains(q), Question.content.contains(q))
    return render_template('index.html', questions=questions)


# 钩子函数(注销)
@app.context_processor
def my_context_processor():
    user_id = session.get('user_id')
    if user_id:
        user = User.query.filter(User.id == user_id).first()
        if user:
            return {'user': user}
    return {}

if __name__ == '__main__':
    app.run(debug=True)
platform.py 视图函数
#encoding: utf-8

from exts import db
from datetime import datetime

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    telephone = db.Column(db.String(11), nullable=False)
    username = db.Column(db.String(50), nullable=False)
    password = db.Column(db.String(100), nullable=False)


class Question(db.Model):
    __tablename__ = 'question'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    # now()获取的是服务器第一次运行的时间
    # now就是每次创建一个模型的时候,都获取当前的时间
    create_time = db.Column(db.DateTime, default=datetime.now)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    author = db.relationship('User', backref=db.backref('questions'))

class Answer(db.Model):
    __tablename__ = 'answer'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    content = db.Column(db.Text, nullable=False)
    create_time = db.Column(db.DateTime, default=datetime.now)
    question_id = db.Column(db.Integer, db.ForeignKey('question.id'))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    question = db.relationship('Question', backref=db.backref('answers', order_by=id.desc()))
    author = db.relationship('User', backref=db.backref('answers'))
models.py
#encoding: utf-8

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
exts.py 配置flask_sqlalchemy操作数据库
#encoding: utf-8

from functools import wraps
from flask import session, redirect, url_for

# 登录限制的装饰器
def login_required(func):

    @wraps(func)
    def qingwa(*args, **kwargs):
        if session.get('user_id'):
            return func(*args, **kwargs)
        else:
            return redirect(url_for('login'))

    return qingwa
decorators.py 登录装饰器

  3、模板文件 (templates 文件夹先创建下面文件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{%  block title %}{% endblock %}-青蛙课堂问答平台</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity=
            "sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

    <script> src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"</script>

    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity=
            "sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin=
            "anonymous"></script>

    <link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}">

    {% block head %}{% endblock %}

</head>
<body>
    <nav class="navbar navbar-default">
              <div class="container">
                <!-- Brand and toggle get grouped for better mobile display -->
                <div class="navbar-header">
                  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                  </button>
                  <a class="navbar-brand" href="#">
                      <img class="logo" src="{{ url_for('static', filename= 'images/qingwalogo.jpg') }}" alt="">
                  </a>
                </div>

                <!-- Collect the nav links, forms, and other content for toggling -->
                <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                  <ul class="nav navbar-nav">
                    <li class="active"><a href="/">首页 <span class="sr-only">(current)</span></a></li>
                    <li><a href="{{ url_for('question') }}">发布问答</a></li>
                  </ul>
                  <form class="navbar-form navbar-left" action="{{ url_for('search') }}" method="get">
                    <div class="form-group">
                      <input name="q" type="text" class="form-control" placeholder="请输入关键字">
                    </div>
                    <button type="submit" class="btn btn-default">查找</button>
                  </form>
                  <ul class="nav navbar-nav navbar-right">
                      {% if user %}
                          <li><a href="#">{{ user.username }}</a></li>
                          <li><a href="{{ url_for('logout') }}">注销</a></li>
                      {% else %}
                          <li><a href="{{ url_for('login') }}">登录</a></li>
                          <li><a href="{{ url_for('regist') }}">注册</a></li>
                      {% endif %}
                  </ul>
                </div><!-- /.navbar-collapse -->
              </div><!-- /.container-fluid -->
            </nav>

            <div class='main'>
                 {% block main %}{% endblock %}
            </div>

</body>
</html>
base.html
{% extends 'base.html' %}

{% block title %}详情-{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/detail.css') }}">

{% endblock %}

{% block main %}
    <h3 class="page-title">{{ question.title }}</h3>
    <p class="question-info">
        <sqan>作者: {{ question.author.username }}</sqan>
        <span>时间: {{ question.create_time }}</span>
    </p>
    <hr>
    <p class="question-content">{{ question.content }}</p>
    <hr>
    <h4>评论: (0)</h4>
    <form action="{{ url_for('add_answer') }}" method="post">
        <div class="form-group">
            <input name="answer_content" type="text" class="from-control" placeholder="请填写评论">
            <input type="hidden" name="question_id" value="{{ question.id }}">
        </div>
        <div class="form-group" style="text-align: right;">
            <button class="btn btn-primary">立即评论</button>
        </div>
    </form>
    <ul class="answer-list">
        {% for answer in question.answers %}
            <li>
                <div class="user-info">
                    <img src="{{ url_for('static', filename='images/qingwalogo.jpg') }}" alt=""
                            class="avatar">
                    <span class="username">{{ answer.author.username }}</span>
                    <span class="create-time">{{ answer.create_time }}</span>
                </div>
                <p class="answer-content">{{ answer.content }}</p>
            </li>
        {% endfor %}
    </ul>
{% endblock %}
detail.html
{% extends 'base.html' %}

{% block title %}首页{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
{% endblock %}

{% block main %}
    <ul>
        {% for question in questions %}
            <li>
                <div class="avatar-group">
                    <img src="{{ url_for('static', filename='images/qingwalogo.jpg') }}"
                         alt="" class="avatar">
                </div>
                <div class="question-group">
                    <p class="question-title"><a href="{{ url_for('detail',question_id=question.id)
                            }}">{{ question.title }}</a></p>
                    <p class="question-content">{{ question.content }}</p>
                    <div class="question-info">
                        <span class="question-author">{{ question.author.username }}</span>
                        <span class="question-time">{{ question.create_time }}</span>
                    </div>
                </div>
            </li>
        {% endfor %}
    </ul>
{% endblock %}
index.html
{% extends 'base.html' %}

{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/login_regist.css') }}">
{% endblock %}

{% block main %}
    <h3 class="page-title">登录</h3>
    <form action="" method="POST">
        <div class='form-container'>
            <div class='form-group'>
                <input type="text" class="form-control" placeholder="手机号码"
                        name="telephone">
            </div>
             <div class='form-group'>
                <input type="password" class="form-control" placeholder="密码"
                        name="password">
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-block">登录</button>
            </div>
        </div>
    </form>

{% endblock %}
login.html
{% extends 'base.html' %}

{% block title %}发布问答{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/question.css') }}">
{% endblock %}

{% block main %}
    <h3 class="page-title">发布问答</h3>
    <form action="" method="post">
        <div class='form-container'>
            <div class="form-group">
                <input type="text" placeholder="请输入标题" name="title"
                        class="form-control">
            </div>
            <div class="form-group">
                <textarea name="content" rows="5" placeholder="请输入内容" class="form-control"></textarea>
            </div>
            <div class="form-group">
                <button class="btn btn-primary">立即发布</button>
            </div>
        </div>
    </form>
{% endblock %}
question.html
{% extends 'base.html' %}

{% block title %}注册{% endblock %}

{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/login_regist.css') }}">
{% endblock %}

{% block main %}
    <h3 class="page-title">注册</h3>
    <form action="" method="POST">
        <div class='form-container'>
            <div class='form-group'>
                <input type="text" class="form-control" placeholder="手机号码"
                        name="telephone">
            </div>
             <div class='form-group'>
                <input type="text" class="form-control" placeholder="用户名"
                        name="username">
            </div>
             <div class='form-group'>
                <input type="password" class="form-control" placeholder="密码"
                        name="password1">
            </div>
            <div class='form-group'>
                <input type="password" class="form-control" placeholder="确认密码"
                        name="password2">
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-block">立即注册</button>
            </div>
        </div>
    </form>

{% endblock %}
regist.html

  4、css文件

.logo{
    width: 30px;
}

body{
    background: #f3f3f3;
}
.main{
    background: #fff;
    width: 730px;
    margin: 0 auto;
    overflow: hidden;
    padding: 10px;
}

.page-title{
    text-align: center;
}
static\css\base.css
.question-info{
    text-align: center;
    margin-top: 20px;

}

.answer-list{
    padding-left: 0;
}
.answer-list li{
    list-style: none;
    border-bottom: 1px solid #eee;
    padding-top: 10px;
}

.avatar{
    width: 40px;
    height: 40px;
    border-radius: 50%;
}

user-info{
    font-size: 16px;
    color: #9b9b9b;
    line-height: 40px;
}

.user-info .username{
    margin-left: 20px;
}

.answer-content{
    margin-left: 60px;
}
.user-info .create-time{
    float: right;
}
static\css\detail.css
ul{
    list-style: none;
    padding-left: 0;
}

ul li{
    border-bottom: 1px solid #eee;
    overflow: hidden;
}
.avatar-group{
    width: 38px;
    height: 100%;
    float: left;
}

.avatar-group .avatar{
    width: 38px;
    height: 38px;
}

.question-group{
    float: left;
    width: 660px;
    margin-left: 10px;
}

.question-title a{
    color: #259;
    font-weight: 900;
}

.question-info{
    text-align: right;
}

.question-info .question-author{
    margin-right: 20px;

}
static\css\index.css
.form-container{
    width: 300px;
    margin: 0 auto;
}
static\css\login_regist.css
.form-container
.form-container{
    width: 500px;
    margin: 0 auto;
    text-align: right;
}
static\css\question.css

  5、创建数据库表命令

      python manage.py db init                    # 创建迁移的仓库

      Python manage.py db migrate            # 创建迁移的脚本

      python manage.py db upgrade            # 更新数据库

      python manage.py runserver               # 运行项目

 1.2 效果展示

   1、登录

       

  2、登录

       

  3、发布问答

      

 

posted @ 2019-02-01 13:59  不做大哥好多年  阅读(313)  评论(0编辑  收藏  举报