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
#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()
#encoding: utf-8 import os DEBUG = True SECRET_KEY = os.urandom(24) SQLALCHEMY_DATABASE_URI = 'mysql://root:1@localhost/testdb' SQLALCHEMY_TRACK_MODIFICATIONS = True
#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)
#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'))
#encoding: utf-8 from flask_sqlalchemy import SQLAlchemy db = 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
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>
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
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; }
.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; }
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; }
.form-container{ width: 300px; margin: 0 auto; }
.form-container .form-container{ width: 500px; margin: 0 auto; text-align: right; }
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、发布问答
作者:学无止境
出处:https://www.cnblogs.com/xiaonq
生活不只是眼前的苟且,还有诗和远方。