python-flask 社交博客案例

需要的模块

pip install flask

pip install  flask-login

pip install flask-sqlalchemy

pip install flask-bootstrap

程序结构

用户登录

1 创建虚拟环境

pipenv install

2 安装需要的模块包

pip install ……

3 根据目录结构创建目录

4 编写setting文件

import os


class BaseConfig(object):
    SECRET_KEY = os.getenv('SECRET_KEY', "key string")
    SQLALCHEMY_TRACK_MODIFICATIONS = False


class DevelopmentConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/bluelog"


class TestingConfig(BaseConfig):
    TESTING = True
    WTF_CSRF_ENABLED = False
    SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/test01"


class ProductionConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/test01"



config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig
}

 5 编写包init文件

log_test/__init__.py

import os
import pymysql
import click
from flask import Flask
from log_test.settings import config
from log_test.extensions import db, bootstrap, login_manager
from log_test.blueprints.admin import ad
from log_test.blueprints.auth import auth
from log_test.blueprints.log import log


def create_app(config_name=None):
    if config_name is None:
        config_name = os.getenv('FLASK_CONFIG', 'development')
    app = Flask("log_test")
    app.config.from_object(config[config_name])

    register_extensions(app)
    register_blueprints(app)
    register_commands(app)

    return app


def register_extensions(app):
    pymysql.install_as_MySQLdb()
    db.init_app(app)
    bootstrap.init_app(app)
    login_manager.init_app(app)


def register_blueprints(app):
    app.register_blueprint(ad, url_prefix='/admin')
    app.register_blueprint(auth, url_prefix='/auth')
    app.register_blueprint(log)


def register_commands(app):

    from log_test.models import User, Admin

    @app.cli.command()
    @click.option('--drop', is_flag=True, help="Create drop delete")
    def initdb(drop):
        if drop:
            db.drop_all()
            click.echo("drop tables")
        db.create_all()
        click.echo("create tables")

6 引入扩展包 extensions

from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager

bootstrap = Bootstrap()
db = SQLAlchemy()
login_manager = LoginManager()


# 设置加载用户的回调函数
@login_manager.user_loader
def load_user(user_id):
    from log_test.models import Admin
    user = Admin.query.get(user_id)
    return user


# 设置登录页端点
login_manager.login_view = "auth.login"

7 编写表单 forms

from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Length, EqualTo


class LoginForm(FlaskForm):
    username = StringField("UserName", validators=[DataRequired(), Length(1, 20)])
    password = PasswordField("PassWord", validators=[DataRequired(), Length(1, 128)])
    remember = BooleanField("Remember Me")
    submit = SubmitField("Log In")


class RegistrationForm(FlaskForm):
    email = StringField("Email")
    username = StringField("UserName", validators=[DataRequired(), Length(1, 20)])
    password = PasswordField("PassWord", validators=[DataRequired(), Length(1, 128), EqualTo('password2')])
    password2 = PasswordField("Confirm PassWord", validators=[DataRequired(), Length(1, 128)])
    submit = SubmitField("Registr")

8 编写模型类 models

from werkzeug.security import generate_password_hash, check_password_hash
from log_test.extensions import db
from flask_login import UserMixin


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), unique=True)
    passwd_hash = db.Column(db.String(128))

    @property
    def passwd(self):
        raise AttributeError('not readable')

    @passwd.setter
    def passwd(self, passwd):
        self.passwd_hash = generate_password_hash(passwd)

    def verify_passwd(self, passwd):
        return check_password_hash(self.passwd_hash, passwd)


class Admin(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20))
    password_hash = db.Column(db.String(128))
    blog_title = db.Column(db.String(60))
    blog_sub_title = db.Column(db.String(100))
    name = db.Column(db.String(30))
    about = db.Column(db.Text)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def validate_password(self, password):
        return check_password_hash(self.password_hash, password)


9 编写蓝图

log_test/blueprints/admin.py
--------------------------
from flask import Blueprint, render_template

ad = Blueprint("admin", __name__)


@ad.route("/")
def index():
    return render_template("admin.html")


log_test/blueprints/auth.py
------------------------
@auth.route("/", methods=('GET', 'POST'))
def login():
    form = LoginForm()
    emsg = ""
    if form.validate_on_submit():
        user_name = form.username.data
        password = form.password.data
        remember = form.remember.data
        admin = Admin.query.filter_by(username=user_name).first()
        if admin:
            if admin.validate_password(password):
                login_user(admin)
                return redirect(request.args.get('next') or url_for('log.index'))
            else:
                emsg = "密码错误"
        else:
            emsg = "账户不存在"
    return render_template("login.html", form=form, emsg=emsg)


@auth.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for('auth.login'))


@auth.route("/registr", methods=('GET', 'POST'))
def registr():
    form = RegistrationForm()
    if form.validate_on_submit():
        user = Admin(username=form.username.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        return redirect(url_for("auth.login"))
    return render_template("registr.html", form=form)


log_test/blueprints/log.py
-----------------------
from flask import Blueprint, redirect, render_template
from flask_login import login_required, current_user

log = Blueprint("log", __name__)


@log.route('/')
@login_required
def index():
    return render_template("index.html", username=current_user.username)

10  编写templates

base.html
--------------

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle"
                    data-toggle="collapse" data-target=".navbar-collapse">
                <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="/">Flasky</a>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="/">Home</a></li>
            </ul>
        </div>
    </div>
</div>
{% endblock %}

{% block content %}
    {% block mycontent %}
    
    {% endblock %}
    <div class="container">
        <div class="page-header">
        {% block page_content %}{% endblock %}
        </div>
    </div>

{% endblock %}



macros.html
------------------
{% macro render_field(field) %} <!-- 定义字段宏 -->
  <dt>{{ field.label }}:
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}




admin.html
------------

{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% block page_content %}
<div class="page-header">
    <h1>Hello, admin</h1>
</div>
{% endblock %}





index.html
-----------
{% extends "base.html" %}
{% import 'macros.html' as macros %}
{% block title %} Flasky {% endblock %}
{% block page_content %}
<div class="page-header">
    <h1>欢迎 {{ username }}!</h1>
    <a href="{{ url_for('auth.logout')}}">登出</a>
</div>
{% endblock %}


login.html
---------------
{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% import 'macros.html' as macros %}
{% block page_content %}
    <form method="POST">
        {{ form.csrf_token }}
        {{ macros.render_field(form.username) }}
        {{ macros.render_field(form.password) }}
        {% if emsg %}  <!-- 如果有错误信息 则显示 -->
            <h3> {{ emsg }}</h3>
        {% endif %}
        <input type="submit" value="登录">
    </form>

    <p>
        New user?
        <a href="{{ url_for('auth.registr') }}">
            click here to registr
        </a>
    </p>
{% endblock %}



registr.html
----------------
{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% import 'macros.html' as macros %}
{% block page_content %}
    <form method="POST">
        {{ form.csrf_token }}
        {{ macros.render_field(form.username) }}
        {{ macros.render_field(form.password) }}
        {{ macros.render_field(form.password2)}}
        {{ macros.render_field(form.email)}}
        {% if emsg %}  <!-- 如果有错误信息 则显示 -->
            <h3> {{ emsg }}</h3>
        {% endif %}
        <input type="submit" value="登录">
    </form>
{% endblock %}

用户角色

1 增加角色模型

models.py
-------

# 在用户模型中增加外键关联角色模型
class Admin(db.Model, UserMixin):
    
    ……
    # 定义角色外键
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    
    # 设置默认角色为User
    def __init__(self, **kwargs):
        super(Admin, self).__init__(**kwargs)
        if self.role is None:
            self.role = Role.query.filter_by(name='User').first()

    # 角色验证
    def can(self, permissions):
        return self.role is not None and (self.role.permissions & permissions) == permissions

    def is_administrator(self):
        return self.can(Permission.ADMINISTER)


# 建立角色模型
class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean, default=False, index=True)
    permissions = db.Column(db.Integer)
    # 建立双向关系
    admins = db.relationship('Admin', backref='role', lazy='dynamic')




# 权限常量表
class Permission:
    FOLLOW = 0x01
    COMMENT = 0x02
    WRITE_ARTICLES = 0x04
    MODERATE_COMMENTS = 0x08
    ADMINISTER = 0x80



# 设置匿名用户权限验证
class AnonymousUser(AnonymousUserMixin):

    def can(self, permissions):
        return False

    def is_administrator(self):
        return False

2 自定义装饰器检查用户角色

decorators.py
------------
from functools import wraps
from flask import abort
from flask.ext.login import current_user


def permission_required(permission):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)

3 在数据库中创建角色

__init__.py

----------------------

def register_commands(app):

    from log_test.models import Admin, Permission, Role, Post

    ……
    @app.cli.command()
    def initroles():
        roles = {
            'User': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES, True),
            'Moderator': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES | Permission.MODERATE_COMMENTS, False),
            'Administor': (0xff, False)
        }
        for r in roles:
            role = Role.query.filter_by(name=r).first()
            if role is None:
                role = Role(name=r)
            role.permissions = roles[r][0]
            role.default = roles[r][1]
            db.session.add(role)
        db.session.commit()


flask initdb --drop 重新创建数据库

flask initroles 初始化角色

4  测试

admin.py
-------

from flask import Blueprint, render_template, request, redirect, url_for
from flask_login import login_required, current_user
from log_test.utols import admin_required, permission_required
from log_test.models import Permission


ad = Blueprint("admin", __name__)


@ad.route('/')
@login_required
@admin_required
def for_admins_only():
    return "For administrators!"


@ad.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderators_only():
    return "For comment moderators!"


# 把 Permission 类加入模板上下文
@ad.app_context_processor
def inject_permissions():
    return dict(Permission=Permission)

5 测试前记得先给用户配置好权限

分页

1 查询出结果

@log.route('/', methods=('GET', 'POST'))
@login_required
def index():
    form = PostForm()

    if form.validate_on_submit():
        post = Post(body=form.body.data,
                    admin=current_user._get_current_object())
        db.session.add(post)
        db.session.commit()
        return redirect(url_for("log.index"))
    page = request.args.get('page', 1, type=int)
    #  paginate() 分页函数: page 页码、 per_page 可选参数默认20,error_out 默认True 超出页面范围返回404错误,false 超出页面范围返回空列表
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'], error_out=False)
    # 返回当前页中内容
    posts = pagination.items
    return render_template("index.html", form=form, posts=posts, pagination=pagination)
    # return render_template("index.html", form=form, posts=posts)

2 编写分页宏模版

macros.html
-----------

{% macro pagination_widget(pagination, endpoint) %}
<ul class="pagination">
  <!-- 判断是否还有上一页 -->
  <li {% if not pagination.has_prev %} class="disabled"{% endif %} >
  <!-- 如果有上一页,拼接url/页码,没有连接变为#   -->
    <a href="{% if pagination.has_prev %}{{ url_for(endpoint,
      page = pagination.page - 1, **kwargs) }}{% else %}#{% endif %}">
      &laquo;
    </a>
  </li>
  <!-- 遍历迭代器 -->
  {% for p in pagination.iter_pages() %}

    {% if p %}
      {% if p == pagination.page %}
      <li class="active">
        <a href="{{ url_for(endpoint, page = p, **kwargs) }}">{{ p }}</a>
      </li>
      {% else %}
      <li>
        <a href="{{ url_for(endpoint, page = p, **kwargs) }}">{{ p }}</a>
      </li>
      {% endif %}
    {% else %}
      <li class="disabled"><a href="#">&hellip;</a></li>
    {% endif %}
  {% endfor %}
   <!-- 判断是否还有下一页 -->
  <li {% if not pagination.has_next %} class="disabled"{% endif %}>
    <!-- 如果有下一页,拼接url/页码,没有连接变为#   -->
    <a href="{% if pagination.has_next %}{{ url_for(endpoint,
      page = pagination.page + 1, **kwargs) }}{% else %}#{% endif %}">
      &raquo;
    </a>
  </li>
</ul>
{% endmacro %}

3 引入宏

index.html
---------------



{% import 'macros.html' as macros %}


…………


<div class="pagination">
{{ macros.pagination_widget(pagination, '.index') }}
</div>
{% endblock %}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值