flask的基本使用

flask的基本使用

顺着视频学习

视频地址:https://ke.qq.com/course/228864#term_id=100270059

开启debug模式

  1. 为什么开启debug模式

    • 原因1:看下面这段代码,很明显除数不能为0,会抛出异常。

      @app.route('/')
      def hello_world():
       a = 1
       b = 0
       c = a / b
       return '你好,世界'

      如果不开启debug,则在网页中显示:(表示是程序内部出错)

      Internal Server Error

      The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.

      网页中不显示是哪里出错,但在代码终端可以看到,但这样很不直观。虽然终端可以直接跳到错误行。

    • 原因2:修改python代码后直接Ctrl + S保存后,对应的页面也会更改,不用重启服务器,更快更方便。

  2. 开启debug的方法

    1. 方法一

      app.run(debug=True)
      
      # 或者
      
      app.debug = True
      app.run()
    2. 方法二:配置参数

      新建一个config.py,然后写DEBUG = True。然后在app.py导入import config并且app.config.from_object(config)

    PS:如果开启不了,请检查editconfigurations中是否开启了flask debug,另外最好single instance

url传参

# 路由里的参数用<>括起来!
@app.route('/article/<id>')
def article(id):
    return '您请求的参数是:' + id
  • 作用:可以在相同的url,但是指定不同的参数,来加载不同的数据
  • 注意:
    1. 路由里的参数用<>括起来!
    2. 视图函数中的参数需要和路由url里的参数名称相同。

url反转

导入:from flask import Flask, url_for

@app.route('/')
def hello_world():
    print(url_for('article', id='1'))
    print(url_for('my_list'))
    return '你好,世界'


# 路由里的参数用<>括起来!
@app.route('/article/<id>')
def article(id):
    return '您请求的参数是:' + id


@app.route('/list/')
def my_list():
    return 'list'
  • 正转:由url得到视图函数
  • 反转:由视图函数得到url
  • 使用到的地方:页面重定向,模板中

重定向redirect

导入:from flask import Flask, url_for, redirect

@app.route('/')
def hello_world():
    print(url_for('article', id='1'))
    print(url_for('my_list'))

    return redirect('/list/')
    return '你好,世界'


# 路由里的参数用<>括起来!
@app.route('/article/<id>')
def article(id):
    return '您请求的参数是:' + id


@app.route('/list/')
def my_list():
    return 'list'
# 更优雅的做法如下:
list_url = url_for('my_list')
return redirect(list_url)
# 这样参数不管怎么变,只要视图函数名不变就可以了

模板

导入:from flask import Flask, render_template

  1. 基本使用:

    • 在模板中新建index.html文件

    • 在视图函数中返回

    • @app.route('/')
      def index():
       return render_template('index.html')
  2. 模板传参:

    @app.route('/')
    def index():
       return render_template('index.html', username='User1', gender='男')
    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <title>主页</title>
    </head>
    <body>
    我是主页
    <p>用户:{{ username }}</p>
    <p>性别:{{ gender }}</p>
    </body>
    </html>

    注意:模板中参数用{{ 参数名 }}包括起来,参数名必须和视图函数中传递的参数名一致

  3. 数据结构传参:

    • 字典:

      @app.route('/')
      def index():
       context = {
           'username': 'User1',
           'gender': '男'
       }
       return render_template('index.html', context=context)
      <!DOCTYPE html>
      <html lang="en">
      <head>
       <meta charset="UTF-8">
       <title>主页</title>
      </head>
      <body>
      我是主页
      <p>用户:{{ context['username'] }}</p>
      <p>性别:{{ context['gender'] }}</p>
      </body>
      </html>

      或者将字典打散

      @app.route('/')
      def index():
       context = {
           'username': 'User1',
           'gender': '男'
       }
       return render_template('index.html', **context)
      <!DOCTYPE html>
      <html lang="en">
      <head>
       <meta charset="UTF-8">
       <title>主页</title>
      </head>
      <body>
      我是主页
      <p>用户:{{ username }}</p>
      <p>性别:{{ gender }}</p>
      </body>
      </html>

      PS:注意字典打散加**号

    • 实例

      @app.route('/')
      def index():
       class Person:
           name = 'Jay'
           age = '20'
      
       p = Person()
      
       context = {
           'username': 'User1',
           'gender': '男',
           'person': p
       }
       return render_template('index.html', **context)
      <body>
      我是主页
      <p>用户:{{ username }}</p>
      <p>性别:{{ gender }}</p>
      <p>姓名:{{ person.name }}</p>
      <p>年龄:{{ person.age }}</p>
      </body>
    • 字典打散后的字典

      @app.route('/')
      def index():
       class Person:
           name = 'Jay'
           age = '20'
      
       p = Person()
      
       context = {
           'username': 'User1',
           'gender': '男',
           'person': p,
           'website': {
               'baidu': 'www.baidu.com',
               'google': 'www.google.com'
           }
       }
       return render_template('index.html', **context)
      <body>
      我是主页
      <p>用户:{{ username }}</p>
      <p>性别:{{ gender }}</p>
      <hr/>
      <p>姓名:{{ person.name }}</p>
      <p>年龄:{{ person.age }}</p>
      <hr/>
      <p>百度:{{ website['baidu'] }}</p>
      <p>谷歌:{{ website['google'] }}</p>
      </body>

      PS:访问字典可以通过params.key的形式或者params[‘key’],推荐使用后者,和字典样式一致

  4. if语句

    @app.route('/<is_login>/')
    def index(is_login):
       if is_login == '1':
           user = {
               'username': 'Jay',
               'age': 8
           }
           return render_template('index.html', user=user)
       else:
           return render_template('index.html')
    <body>
    {% if user %}
       <a href="#">{{ user['username'] }}</a>
       <a href="#">注销</a>
    {% else %}
       <a href="#">登录</a>
       <a href="#">注册</a>
    {% endif %}
    
    </body>

    PS:注意,return render_template(‘index.html’, user=user)传参给模板不能直接打散,否则得不到该变量

  5. for语句

    • 字典的遍历

      @app.route('/')
      def index():
       user = {
           'username': 'Jay',
           'age': 20
       }
       for k, v in user.items():
           print(k)
           print(v)
       return render_template('index.html', user=user)
      <body>
      {% for k, v in user.items() %}
       <p>{{ k }}:{{ v }}</p>
      {% endfor %}
      </body>
    • 列表的遍历

      @app.route('/')
      def index():
       context = {
           'user': {
               'username': 'Jay',
               'age': 20,
           },
           'website': [
               'www.baidu.com',
               'www.google.com'
           ]
       }
       for website in context['website']:
           print(website)
       return render_template('index.html', **context)
      <body>
      {% for k, v in user.items() %}
       <p>{{ k }}:{{ v }}</p>
      {% endfor %}
      
      <hr>
      {% for website in website %}
       <p>{{ website }}</p>
      {% endfor %}
      </body>
  6. 过滤器

    • default过滤器:

      def index():
       context = {
           'user': {
               'username': 'Jay',
               'age': 20,
           },
           'website': [
               'www.baidu.com',
               'www.google.com',
               'www.souhu.com'
           ],
           'avatar': 'https://avatar.csdn.net/B/A/9/3_towtotow.jpg'
       }
       for website in context['website']:
           print(website)
       return render_template('index.html', **context)
      <body>
      {% for k, v in user.items() %}
       <p>{{ k }}:{{ v }}</p>
      {% endfor %}
      
      <hr>
      {% for website in website %}
       <p>{{ website }}</p>
      {% endfor %}
      <hr>
      <img src="{{ avatar|default('https://avatar.csdn.net/D/B/C/3_core___java.jpg') }}">
      </body>

      PS:default过滤器只是其中一个过滤器而已,作用是后台传的变量不存在,就用default展示。但是如果变量存在,值为空,那么过滤器不会用默认值来展示,这样展示的是后台传过来的空值

    • length过滤器:求字符串、列表、元祖、字典的长度

      @app.route('/')
      def index():
       website = [
                     'www.baidu.com',
                     'www.google.com',
                     'www.souhu.com'
                 ]
       return render_template('index.html', website=website)
      <body>
      网站数:{{ website|length }}
      </body>

      PS:如果后台没传数据,则过滤为0,传了就是数据的长度,这个不用担心数据为空,为空就是0。

  7. 继承和block

    1. 首先创建一个base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      
        <style>
            .nav {
                background: #3a3a3a;
                height: 65px;
            }
      
            ul {
                overflow: hidden;
            }
      
            ul li {
                float: left;
                list-style: none;
                padding: 0 10px;
                line-height: 65px;
            }
      
            ul li a {
                color: #fff;
            }
        </style>
      </head>
      <body>
      <div class="nav">
        <ul>
            <li>
                <a href="#">首页</a>
            </li>
            <li>
                <a href="#">发布评论</a>
            </li>
        </ul>
      </div>
      
      {% block main %}{% endblock %}
      </body>
      </html>
      • 定义了一个导航条,所有子页面均可以继承使用
      • 通过{% block main %}{% endblock %} block语句,提供给子页面main块的接口来实现自己的内容
    2. python

      @app.route('/')
      def index():
        return render_template('index.html')
      
      
      @app.route('/login/')
      def login():
        return render_template('login.html')
    3. index.html

      <body>
      {% extends 'base.html' %}
      
      {% block main %}
        <h1>这是主页面</h1>
      {% endblock %}
      </body>
    4. login.html

      <body>
      {% extends 'base.html' %}
      
      {% block main %}
        <h1>这是登录页面</h1>
      {% endblock %}
      </body>
  8. url_for链接和静态文件使用,重构上述代码:

    1. 在static下新建 css 文件夹,在css文件夹下新建base.css文件,将base.html的head中设置的style复制到base.css中去,让base.html的head标签中引入base.css属性。

      base.css

      .nav {
        background: #3a3a3a;
        height: 65px;
      }
      
      ul {
        overflow: hidden;
      }
      
      ul li {
        float: left;
        list-style: none;
        padding: 0 10px;
        line-height: 65px;
      }
      
      ul li a {
        color: #fff;
      }

      base.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      
        <link rel="stylesheet" href="{{ url_for('static', filename='css/base.css') }}"/>
      </head>
      <body>
      <div class="nav">
        <ul>
            <li>
                <a href="{{ url_for('index') }}">首页</a>
            </li>
            <li>
                <a href="{{ url_for('login') }}">登录</a>
            </li>
        </ul>
      </div>
      
      {% block main %}{% endblock %}
      </body>
      </html>

      python代码和index.html以及login.html不变

      PS:加载图片,请求项目URI都可以用url_for来加载

数据库

  • python连接数据库的安装与配置

    1. 下载安装mysql
    2. 下载mysql_python驱动(直接百度),Windows系统下不能直接pip安装,下载后点击安装包安装,貌似不起作用
    3. 虚拟环境中安装sqlalchemy
    4. 代码中配置mysql数据库,然后报错:ModuleNotFoundError: No module named ‘MySQLdb’
    5. 解决办法在虚拟环境中安装:pip install mysqlclient,然后一切OK
  • 代码中连接mysql

    配置文件

    
    # 配置参数
    
    DEBUG = True
    
    DIALECT = 'mysql'
    DRIVER = 'mysqldb'
    USERNAME = 'root'
    PASSWORD = '1234'
    HOST = '127.0.0.1'
    PORT = '3306'
    DATABASE = 'test'
    
    
    # SQLALCHEMY标志URI
    
    SQLALCHEMY_DATABASE_URI = "{}+{}://{}:{}@{}:{}/{}?charset=utf8".format(DIALECT, DRIVER, USERNAME,
                                                                         PASSWORD, HOST, PORT,
                                                                         DATABASE)
    
    # 忽略警告
    
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    主代码:

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    import config
    
    app = Flask(__name__)
    app.config.from_object(config)
    
    db = SQLAlchemy(app)
    
    
    class Student(db.Model):
      __tablename__ = 'student'
      number = db.Column(db.Integer, primary_key=True, autoincrement=True)
      name = db.Column(db.String(100), nullable=False)
    
    
    db.create_all()
    
    
    @app.route('/')
    def index():
      return 'index'
    
    
    if __name__ == '__main__':
      app.run()

    注意:

    1. 模型类需要继承自db.Model,这个db变量时SQLAlchemy的实例。然后类的每一个成员属性都需要和表一一对应来映射,必须初始化成db.Column的类型
    2. 数据类型:
      • db.Integer表示整型
      • db.String(length)表示varchar类型
      • db.Text表示text类型
    3. 其他参数
      • ’primary_key’:表示主键
      • ‘autoincrement’:表示整型自增
      • ‘nullable’:设置True或False表示是否为空
  • 增:

    
    # 创建一个映射实例
    
    student1 = Student(name='Jay')
    
    # 增加
    
    db.session.add(student1)
    
    # 提交
    
    db.session.commit()
  • 查:

    返回数组:

    
    # 按条件返回一个数组
    
    students = Student.query.filter(Student.name == 'Jay').all()
    
    # 取到数组的第一个元素
    
    student = students[0]
    
    
    #复杂查询
    
      # 查询或
      search_questions = Question.query.filter(
          or_(Question.title.contains(q), Question.content.contains(q))).order_by(
          '-create_time').all()
      # 查询与
      Question.query.filter(Question.title.contains(q), Question.content.contains(q))

    返回查到的第一个元素:

    
    # 只查找匹配的第一条
    
    student = Student.query.filter(Student.name == 'Jay').first()
  • 
    # 修改
    
    
    # 1.先把要修改的数据查出来
    
    student = Student.query.filter(Student.name == 'Jay').first()
    
    # 2.修改
    
    student.name = 'new name'
    
    # 3.提交
    
    db.session.commit()
  • 
    # 删除
    
    
    # 1.把需要删除的数据查出来
    
    student = Student.query.filter(Student.name == 'Jay').first()
    
    # 2.删除
    
    db.session.delete(student)
    
    # 3.提交
    
    db.session.commit()
  • 高级用法之外键,反向引用

    from datetime import datetime
    
    from exts import db
    
    
    class User(db.Model):
      __table_name__ = 'user'
      id = db.Column(db.Integer, primary_key=True, autoincrement=True)
      phone = 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):
      __table_name__ = '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)
      # 外键为user表中的id字段
      author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
      # 按外键反转查找author信息,关系到User模型。然后反向引用得到该author的所有questions
      author = db.relationship('User', backref=db.backref('questions'))
  • 一对多关系,引入中间表

    from datetime import datetime
    
    from werkzeug.security import generate_password_hash, check_password_hash
    
    from exts import db
    
    
    
    # 用户
    
    class User(db.Model):
      __table_name__ = 'user'
      id = db.Column(db.Integer, primary_key=True, autoincrement=True)
      username = db.Column(db.String(50), nullable=False)
      email = db.Column(db.String(50))
    
    
    
    # 文章tag的中间表
    
    article_tag_table = db.Table('article_tag',
                               db.Column('article_id', db.Integer, db.ForeignKey('article.id'),
                                         primary_key=True),
                               db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'),
                                         primary_key=True))
    
    
    
    # 文章
    
    class Article(db.Model):
      __table_name__ = 'article'
      id = db.Column(db.Integer, primary_key=True, autoincrement=True)
      title = db.Column(db.String(100))
      content = db.Column(db.Text)
      author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    
      author = db.relationship('User', backref='articles')
      tags = db.relationship('Tag', secondary=article_tag_table, backref='tags')
    
    
    
    # 文章标签
    
    class Tag(db.Model):
      __table_name__ = 'tag'
      id = db.Column(db.Integer, primary_key=True, autoincrement=True)
      name = db.Column(db.String(50))
      article_id = db.Column(db.Integer, db.ForeignKey('article.id'))
    @app.route('/')
    def hello_world():
      user = User(username='lzj', email='123@qq.com')
      article = Article(title='title123', content='content123')
      article.author = user
      tag1 = Tag(name='前端')
      tag2 = Tag(name='python')
      article.tags.append(tag1)
      article.tags.append(tag2)
    
      # 这里数据库操作增加一个article,那么与该article相关的表会自动映射更新
      db.session.add(article)
      db.session.commit()
      return 'Hello World!'
  • flask-script

    • 虚拟环境中安装:pip install flask-script

    • 用法:请看视频

  • 分开modules解决循环引用

    新建一个exts.py文件

    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()

    新建model.py文件

    from exts import db
    
    
    class Student(db.Model):
      __tablename__ = 'student'
      number = db.Column(db.Integer, primary_key=True, autoincrement=True)
      name = db.Column(db.String(100), nullable=False)

    app.py引用如下

    from flask import Flask
    from exts import db
    from models import Student
    
    import config
    
    app = Flask(__name__)
    app.config.from_object(config)
    
    # 数据库迁移后,不要忘记初始化
    
    db.init_app(app)
    
    
    # 否则只能在视图函数中执行,不能在全局执行
    
    with app.app_context():
      db.create_all()
    
    
    @app.route('/')
    def index():
      return 'index'
    
    
    if __name__ == '__main__':
      app.run()
  • flask-migrate

    1. 虚拟环境中安装:pip install flask-migrate

    2. 作用:数据库的迁移,假如对数据库的字段属性进行了更改,数据库是不会映射的。比如增加一个字段,这时可以直接将表drop掉,但是用户的数据就没了,显然很不好。于是可以采用migrate。

    3. 用法:

      • 写好manage.py后,进入虚拟环境并activate,然后在虚拟环境中用cd..回退,进入到项目目录中命令:

      python manage.py db init

      初始化migrate

      • 然后命令:

      python manage.py db migrate

      生成一个迁移文件

      • 然后实时迁移更新数据库

      python manage.py db upgrade

    4. 数据库迁移后不要忘记在app.py中初始化:db.init_app(app)

session

  • flask中session机制:

    1. flask中session机制是:把敏感数据经过加密后放入‘session’中,再把’session’存放到’cookie’中,下次请求的时候,再从浏览器发送过来的’cookie’中读取’session’,然后再从’session’中读取敏感数据,并进行解密,获取最终的用户数据。
    2. flask的这种’session’机制,可以节省服务器的开销,因为把所有的信息都存储到了客户端(浏览器)。
    3. 但安全是相对的,把’session’放到’cookie’中,经过加密,也是比较安全的。
  • session的使用:

    1. 在config.py中设置SECRET_KEY:24位的随机字符码

      
      # session
      
      SECRET_KEY = os.urandom(24)
    2. 导入session:from flask import Flask, session

      • 设置session:
      
      # session设置一个key-value
      
      session['username'] = 'jay'
      session['password'] = '1399'
      • 访问session:
         # 访问字典元素可以用session['key']也可以session.get('key')
         # 区别是前者没有查找到会出异常KeyError,后者返回None
         print(session['username'])
         # 推荐使用第二种方式,即使不存在也不会抛出异常
         print(session.get('password'))
         if not session.get('user'):
             print('user为None')
      • 删除session:
      
      # 如果key不存在会抛出异常KeyError
      
      
      # del session['user_id']
      
      
      # session.pop('user')
      
      
      
      # 设置第二个参数后,即使user_id不存在也不会出现Key_Error
      
      session.pop('username'None)
      
      # 清空本项目所有session数据
      
      session.clear()
    3. 设置session的过期时间:

      • 默认的session的过期时间为:关闭会话(浏览器关闭)

      • 设置permanent后过期时间为一个月(31天)

      session.permanent = True
      • 在配置文件中修改permanent时间
      PERMANENT_SESSION_LIFETIME = timedelta(days=25)

get请求和post请求

  • 使用场景和传参方式

    1. get请求

      使用场景:如果只对服务器获取数据,并没有对服务器产生影响,那么这个时候用get请求。

      传参方式:get请求的参数放在url中,通过’?’形式来指定key-value

    2. post请求

      使用场景:如果对服务器产生影响,如上传数据,那么用post请求。

      传参方式:post请求参数不是放在url中,是通过’form data’的形式发送给服务器的

  • get请求基本用法

    index.html中定义一个链接

    <body>
    <a href="{{ url_for('search', q='hello') }}">跳转到搜索页面</a>
    </body>

    搜索页面的视图函数

    @app.route('/search/')
    def search():
      # 得到一个key-value传参字典
      arguments = request.args
      print(arguments)
      print(arguments.get('q'))
      return 'search'
  • post请求基本用法

    login.html中定义一个表单

    <body>
    {#如果不写method="post"的话,表单默认使用get请求#}
    {#action表示跳转到指定页面(不写就是本页面)如果指定的页面没有定义post请求方法会报Method Not Allowed#}
    <form action="{{ url_for('login') }}" method="post">
      <table>
          <tbody>
          <tr>
              <td>用户名:</td>
              {#name可理解为post请求的key,输入框输入的内容未value#}
              <td><input type="text" placeholder="请输入用户名" name="username"></td>
          </tr>
          <tr>
              <td>密码:</td>
              <td><input type="text" placeholder="请输入密码" name="password"></td>
          </tr>
          <tr>
              <td></td>
              <td><input type="submit" value="登录"></td>
          </tr>
          </tbody>
      </table>
    </form>
    </body>

    login视图函数:

    
    # 默认的视图函数只能使用get请求
    
    
    # 如果用post请求需要定义
    
    @app.route('/login/', methods=['GET', 'POST'])
    def login():
      # 判断用的是get方法还是post方法
      if request.method == 'GET':
          return render_template('login.html')
      else:
          form = request.form
          print('账号:' + form.get('username'))
          print('密码:' + form.get('password'))
          return '得到参数:' + str(request.form)

    注意:这里login在浏览器中回车访问是get请求,返回一个视图。而表单提交后是post请求,返回一个字符串视图

保存全局变量的g对象

  • g:global
  • g对象可以存储任何数据,也可以用来保存用户数据
  • g对象在一次请求中所执行的代码的地方都是可以用的,但是这一次请求过后就销毁了。

g对象的使用:

login.html

<body>
{#如果不写method="post"的话,表单默认使用get请求#}
{#action表示跳转到指定页面,不写就是本页面如果指定页面没有定义post请求方法会报Method Not Allowed#}
<form action="{{ url_for('login') }}" method="post">
    <table>
        <tbody>
        <tr>
            <td>用户名:</td>
            {#name可理解为post请求的key,输入框输入的内容未value#}
            <td><input type="text" placeholder="请输入用户名" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="text" placeholder="请输入密码" name="password"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="登录"></td>
        </tr>
        </tbody>
    </table>
</form>
</body>

login视图函数:

# 默认的视图函数只能使用get请求
# 如果用post请求需要定义
@app.route('/login/', methods=['GET', 'POST'])
def login():
    # 判断用的是get方法还是post方法
    if request.method == 'GET':
        return render_template('login.html')
    else:
        form = request.form
        username = form.get('username')
        password = form.get('password')
        if '1' == username and '1' == password:
            # 记录登录信息
            g.username = username
            login_log()
            return '登录成功'
        else:
            return '登陆失败'

钩子函数

  1. before_request

    • 在请求之前执行(所有请求),即在视图函数之前执行

    • 这个函数用一个装饰器修饰 @app.before_request

    • 应用场景:

      可以和g对象一起使用,即每一次请求都调用一次这个before_request钩子函数,然后将session的数据保存到g对象中,由于每一次请求之前调用,所以g对象包含的session信息在每次请求前都存在,不会在下一次请求后就销毁。比如:

      用户登录后一般在session中存一个user_id,然后用这个user_id来在数据库中查找User信息,如果每一次都查找数据库会很慢,这样可以在before_request钩子函数中将session中的user_id取出来,然后在数据库中查找User信息,然后将这个user信息保存到g对象中去,这样每次请求都有这个user信息了,不用每次都访问数据库。

      (但是有个疑问,before_request是在每次请求之前都调用,那么每次都访问数据库将user信息存到g对象中不是比需要user信息的时候在视图函数中访问数据库的次数不是多多了?这个疑问后来懂了再补上

      视图函数:虽然g对象在一次请求过后就会被销毁,但是因为每次请求前(before_request)都访问了session然后将需要的信息存到g对象中,所以g对象被销毁了又在钩子函数中创建,所以每个视图函数都可以使用g对象。

      @app.route('/')
      def index():
       print('index, g.username == ' + g.username)
       return render_template('index.html')
      
      
      
      # 默认的视图函数只能使用get请求
      
      
      # 如果用post请求需要定义
      
      @app.route('/login/', methods=['GET', 'POST'])
      def login():
       # 判断用的是get方法还是post方法
       if request.method == 'GET':
           return render_template('login.html')
       else:
           form = request.form
           username = form.get('username')
           password = form.get('password')
           if '1' == username and '1' == password:
               # 记录登录信息
               session['username'] = username
               return redirect(url_for('index'))
           else:
               return '登陆失败'
      
      @app.before_request
      def my_before_request():
       print('before_request钩子函数执行了')
       if session.get('username'):
           g.username = session.get('username')
      
      
      @app.route('/edit/')
      def edit():
       if hasattr(g, 'username'):
           return 'edit user information'
       else:
           return redirect(url_for('login'))

      login.html和上面的一样就不复制了

  2. context_processor

    • 使用场景:渲染的模板的多个页面需要相同的数据

    • 注意:必须有返回值,即使返回空字典{}也要返回,否则报错

    • 用法如下:

      
      # 钩子函数返回一个字典,这个字典在所有模板可用
      
      
      # 从打印结果来看,这个钩子函数应该是在视图函数末尾执行
      
      
      # 不能没有返回值,任何一种情况都必须返回一个字典,否则报错
      
      @app.context_processor
      def my_context_processor():
       print('my_context_processor钩子函数执行了')
       username = session.get('username')
       if username:
           return {'username': '111'}
       else:
           return {}

      一个少处理返回情况的异常:TypeError: ‘NoneType’ object is not iterable

      @app.context_processor
      def my_context_processor():
       user_id = session.get('user_id')
       # 这是两种情况,其中一种没处理返回就报:TypeError: 'NoneType' object is not iterable
       if user_id:
           user = User.query.filter(User.id == user_id).first()
           if user:
               return {'user': user}
       return {}

创建一个项目必须注意的点

  1. 配置文件

    debug调试,数据库,session等等

    import os
    from datetime import timedelta
    
    
    # debug模式
    
    DEBUG = True
    
    
    # 配置数据库
    
    DIALECT = 'mysql'
    DRIVER = 'mysqldb'
    USERNAME = 'root'
    PASSWORD = '1234'
    HOST = '127.0.0.1'
    PORT = '3306'
    DATABASE = 'test'
    
    # SQLALCHEMY标志URI
    
    SQLALCHEMY_DATABASE_URI = "{}+{}://{}:{}@{}:{}/{}?charset=utf8" \
       .format(DIALECT, DRIVER, USERNAME,
               PASSWORD, HOST, PORT,
               DATABASE)
    
    # 忽略SQLALCHEMY警告
    
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    
    
    # session
    
    
    # 设置session'盐':SECRET_KEY
    
    SECRET_KEY = os.urandom(24)
    
    # 设置session过期时间,25天,设置permanent默认一个月,没设置关闭浏览器就销毁
    
    PERMANENT_SESSION_LIFETIME = timedelta(days=25)
  2. 导包,各种包

    from flask import Flask, url_for, render_template, request, session, g, redirect
  3. 文件夹管理等等

  4. 具体看问答平台小案例

坑,各种坑

  1. flask debug模式怎么开都开不了的坑

    点run左边的project->EditConfiguration,勾选FLASK_DEBUG,另外如果只想启动一个服务,不用在控制台弹出多个,点击右上角的Single instance only

  2. 配置虚拟环境

    需要的各种包都在虚拟环境中安装,创建项目的时候注意选择虚拟环境即可

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值