BZ易风

导航

 

模板

alchemy:基础功能加数据库--常用

starter:只包含pyramid最基本功能,简单模板,无数据库

zodb:

创建项目

pcreate -s alchemy 项目名称

 项目创建完成后进入到项目路径

python setup.py develop  # 不会把代码复制过去,会在site-packages里创建一个路径,还是回去项目目录里找代码

python setup.py install  # 把项目需要的依赖全部复制到site-packages里,就算是把项目目录删掉都可以用,会导致项目里改代码的话不生效

查看MyProject.egg-link

/home/python/.virtualenv/虚拟环境/lib/python2.7/site-packages

这里有MyProject.egg-link和其它的一些依赖

依赖包

 打开debug调试

在development.ini中打开

debugtoolbar.hosts = 192.168.2.3  # 输入想在哪个ip调试

 development.ini详细介绍

###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###

[app:main]  # 一个中括号是一个节
use = egg:MyProject  # egg后面跟项目名称

pyramid.reload_templates = true  # 对模板进行修改后是否自动重载,开发阶段可以用来调试,正式环境会降低渲染速度 
pyramid.debug_authorization = false  # 认证和权限相关的调试信息
pyramid.debug_notfound = false  # 找不到页面的时候提示相关信息
pyramid.debug_routematch = false  # url映射机制调试
pyramid.default_locale_name = en  # 程序默认语言
pyramid.includes =        # 可以多条 加载pyramid相关插件
    pyramid_debugtoolbar
    pyramid_tm

sqlalchemy.url = sqlite:///%(here)s/devdata.db
# sqlalchemy.url = mysql://root:mysql@localhost:3306/pyramid_myproject?charset=utf8
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1  # 调试工具条 只能在本地使用

###
# wsgi server configuration
###

[server:main]
use = egg:waitress#main
host = 192.168.230.128  # 监听ip
port = 5678  # 端口

###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###

[loggers]
keys = root, myproject, sqlalchemy

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console

[logger_myproject]
level = DEBUG
handlers =
qualname = myproject

[logger_sqlalchemy]
level = INFO
handlers =
qualname = sqlalchemy.engine
# "level = INFO" logs SQL queries.
# "level = DEBUG" logs SQL queries and results.
# "level = WARN" logs neither.  (Recommended for production systems.)

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s

 初始化数据

initialize_[项目名称]_db development.ini

存储模型设计

model.py

class Users(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)  # Column固定用法
    name = Column(Unicode(255), unique=True)
    password = Column(Unicode(255))
    email = Column(Unicode(255), unique=True)
    group_id = Column(Integer)

    def __init__(self, name, password, email, group_id):
        self.name = name
        self.password = password
        self.email = email
        self.group_id = group_id

 注册到initializedb.py中

import os
import sys
import transaction

from sqlalchemy import engine_from_config

from pyramid.paster import (
    get_appsettings,
    setup_logging,
    )

from ..models import (
    DBSession,
    MyModel,
    Users,
    Base,
    )


def usage(argv):
    cmd = os.path.basename(argv[0])
    print('usage: %s <config_uri>\n'
          '(example: "%s development.ini")' % (cmd, cmd))
    sys.exit(1)


def main(argv=sys.argv):
    if len(argv) != 2:
        usage(argv)
    config_uri = argv[1]
    setup_logging(config_uri)
    settings = get_appsettings(config_uri)
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.create_all(engine)
    with transaction.manager:
        # model = MyModel(name='one', value=1)
        # DBSession.add(model)
        admin = Users()
        admin.name = 'admin'
        admin.password = 'admin'
        admin.email = '88983860@qq.com'
        DBSession.add(admin)

删除原来的devdata.db,重新初始化数据库

 数据存储类型设计

多对多,多表关联

自关联

# -*- coding:UTF-8 -*-
from datetime import datetime
from sqlalchemy import (
    Table,          # 创建关联表的时候要导入
    Column,
    ForeignKey,     # 引用外键
    Integer,        # 整型
    Text,           # 文本类型
    Unicode,        # Unicode类型
    DateTime,       # 时间类型
    Float,          # 浮点型
    )

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import (
    scoped_session,
    sessionmaker,
    relationship,  # 引用关联数据
    )

from zope.sqlalchemy import ZopeTransactionExtension

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)  # primary_key 主键
    name = Column(Unicode(255), nullable=False, unique=True)  # nullable=False
                                                              # 不允许为空 unique 唯一
    password = Column(Unicode(255), nullable=False)
    email = Column(Unicode(255), unique=True)
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)  # 关联外键groups的id
    group = relationship('Group', backref='users')
    # 可以使用User的实例对象+".group"查询Group的数据,\
    #  backref可以使用Group的实例对象查询User的数据,\
    # 以列表形式返回 相当于在Group里写users = relationship('User')

# 创建关联中间表
# 中间表 = Table(表名, Base.metadata,字段1,字段2....)
# Base.metadata 固定用法
group_permisson = Table('group_permission', Base.metadata,
    Column('group_id', Integer, ForeignKey('groups.id'), primary_key=True),  # 字段前要加字段名称
    Column('permission_id', Integer, ForeignKey('permissions.id'),
           primary_key=True)
                        )

class Group(Base):
    __tablename__ = 'groups'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), nullable=False)

    permission = relationship('Permission', secondary='group_permission',  # 从表关联
                              backref='groups')

class Permission(Base):
    __tablename__ = 'permissions'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), nullable=False)

class Item(Base):
    __tablename__ = 'items'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), nullable=False, unique=True)
    description = Column(Text)
    price = Column(Float, nullable=False, default=0.00)

    category_id = Column(Integer, ForeignKey('categories.id'), nullable=False)
    category = relationship('Category', backref='items')

class Category(Base):
    __tablename__ = 'categories'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode(255), nullable=False, unique=True)
    parent_id = Column(Integer, ForeignKey('categories.id'), nullable=True)
    parent = relationship('Category', remote_side=[id],   # 外键是自己的时候需要加入remote_side
                          backref='children')

class ItemImage(Base):
    __tablename__ = 'images'
    id = Column(Integer, primary_key=True)
    path = Column(Unicode(255), nullable=False)
    item_id = Column(Integer, ForeignKey('items.id'), nullable=False)
    item = relationship('Item', backref='images')

class Comment(Base):
    __tablename__ = 'comments'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    user = relationship('User', backref='comments')
    item_id = Column(Integer, ForeignKey('items.id'), nullable=False)
    item = relationship('Item', backref='comments')

    rank = Column(Integer, nullable=False, default=3)
    content = Column(Text)

cart_item = Table('cart_item', Base.metadata,
    Column('cart_id', Integer, ForeignKey('carts.id'), primary_key=True),
    Column('item_id', Integer, ForeignKey('items.id'), primary_key=True)
                  )

class Cart(Base):
    __tablename__ = 'carts'
    id = Column(Integer, primary_key=True)

    items = relationship('Item', secondary='cart_item')
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    user = relationship('User', backref='cart')

order_item = Table('order_item', Base.metadata,
    Column('order_id', Integer, ForeignKey('orders.id'), primary_key=True),
    Column('item_id', Integer, ForeignKey('items.id'), primary_key=True)
               )

class Order(Base):
    __tablename__ = 'orders'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    user = relationship('User')

    items = relationship('Item', secondary='order_item')
    add_time = Column(DateTime, nullable=False, default=datetime.now())
    address = Column(Unicode(255), nullable=False)
    telephone = Column(Unicode(25), nullable=False)

 配置路由

 

 __init__.py

# -*- coding:UTF-8 -*-
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from .models import (
    DBSession,
    Base,
    )


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
    config.add_route('home', '/')  # url映射 对应home主页
    config.add_route('category', '/category')  # url映射 对应category页面
    config.scan()
    return config.make_wsgi_app()

视图函数views.py函数中

# -*- coding:UTF-8 -*-
from pyramid.response import Response
from pyramid.view import view_config


from .models import (
    DBSession,
    )


@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
    # try:
    #     one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
    # except DBAPIError:
    #     return Response(conn_err_msg, content_type='text/plain', status_int=500)
    return {'one': 'one', 'project': '我是天才'}


@view_config(route_name='category', renderer='string')  # route_name对应__init__.py中的config.add_route('category', '/category')
def category_view(request):
    return 'This is category!'

使用视图类view_defaults

创建views文件夹

 

 创建base.py文件

import logging
from pyramid.httpexceptions import HTTPFound, HTTPBadRequest, HTTPServerError, \
    HTTPForbidden, HTTPUnauthorized
import json

log = logging.getLogger(__name__)


class Base(object):
    def __init__(self, request):
        self.request = request


class CBase(Base):
    def __init__(self, request):
        Base.__init__(self, request)

创建视图控制器categories.py

# -*- coding:UTF-8 -*-
from pyramid.response import Response
from pyramid.view import view_config, view_defaults
from myshop.lib import category  # 引用category数据文件
from base import CBase

ctrl = 'categories'

# @view_config(route_name='home', renderer='templates/mytemplate.pt')
@view_defaults(route_name='/')  # url映射对应__init__.py中的config.add_route('/', '/{ctrl}/{action}')
class categories(CBase):
    def __init__(self, request):
        CBase.__init__(self, request)
        self.request.title = '分类'

    @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),  # 对应控制器文件和方法
                 renderer="mytemplate.html")
    def view(request):
        category_list = category.get_category_list()
        return {'one': 'one', 'project': category_list}

__init__.py文件中修改路由

# -*- coding:UTF-8 -*-
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from myshop.models import (
    DBSession,
    Base,
    )


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.add_renderer(".html", 'pyramid.mako_templating.renderer_factory')
    config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
    # config.add_route('home', '/')  # url映射 对应home主页
    # config.add_route('category', '/category')  # url映射 对应category页面
    config.add_route('/', '/{ctrl}/{action}')  # url映射 对应category页面
    config.scan()
    return config.make_wsgi_app()
带参数路由

配置url映射

config.add_route('/', '/{ctrl}/{action}/{id}')  # url映射 携带id参数

视图函数views/item.py中接收参数

# -*- coding:UTF-8 -*-
from pyramid.response import Response
from pyramid.view import view_config, view_defaults
from myshop.lib import category
from base import CBase

ctrl = 'item'

# @view_config(route_name='home', renderer='templates/mytemplate.pt')
@view_defaults(route_name='/')
class item(CBase):
    def __init__(self, request):
        CBase.__init__(self, request)
        self.request.title = '商品'

    @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                 renderer="item.html")
                 # renderer="string")
    def view(self):
        id = self.request.matchdict.get('id')  # 接收id
        result = {}
        result['id'] = id
        return result

渲染到模板item.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>${id}</h1>
</body>
</html>

 

模板引擎换成mako以及后缀换成'.html'

1. 在配置文件development.ini中,添加上:

mako.directories = [project name]:[root path]

mako.directories = myshop:templates  # 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录
mako.strict_undefined = true

project name是你项目的名称

root path 是你模板文件存放的根目录

跟多关于mako的设置: https://pyramid.readthedocs.io/en/1.3-branch/narr/environment.html#mako-template-render-settings

2. 修改项目的__init__.py文件,在main函数中添加上:

config.add_renderer('.html', 'pyramid.mako_templating.renderer_factory')

凡是使用.html结尾的模板,都会使用mako引擎

3. 当在View.py中,使用.html的模板,就会使用mako模板引擎了。

@view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                 renderer="mytemplate.html")
    def view(request):
        # try:
        #     one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
        # except DBAPIError:
        #     return Response(conn_err_msg, content_type='text/plain', status_int=500)
        return {'one': 'one', 'project': 'TTTTT'}

 出错

File "/home/python/.virtualenvs/pyramid_py2/local/lib/python2.7/site-packages/mako/lookup.py", line 263, in get_template
    "Cant locate template for uri %r" % uri
TopLevelLookupException: Cant locate template for uri 'mytemplate.html'

development.int文件里mako配置的时候不能在后面加注释

mako.directories = myshop:templates  # 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录

导致找不到模板

# 更改为mako模板 mako.directories = [project name]:[root path] 项目名:html文件目录
mako.directories = myshop:templates

解决

认证和权限

配置__init__.py

# -*- coding:UTF-8 -*-
from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy  # 认证
from pyramid.authorization import ACLAuthorizationPolicy        # 权限
from pyramid.security import Allow
from sqlalchemy import engine_from_config
from myshop.lib import user
from myshop.models import (
    DBSession,
    Base,
    )

def groupfinder(userid, request):
    """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
    print("*" * 100)
    user_info = user.get_user_by_id(userid)  # 查询登录账户信息
    if user_info:
        return [user.group.id]
    return None

class RootFactory(object):   # 创建RootFactory类
    def __init__(self, request):
        """读取所有权限"""
        group_list = user.get_group_list()  # 查询所有的组
        self.__acl__ = []  # 准备acl列表
        for group in group_list:
            for permission in group.permissions:
                # 给acl列表添加元祖,  1.Allow 允许或拒绝 需要导入Allow库 2.为了不和其它id冲突 添加g:的标识符 3.权限名称
                self.__acl__.append(
                    (Allow, 'g:' + str(group.id), permission.name)
                )


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    # 权限和授权
    # 认证机制
     authn_policy = AuthTktAuthenticationPolicy(
                 'secret',  # 加密密钥
                 callback = groupfinder,  # 用于查询用户属于哪个用户组,以及这个用户是否存在
                 hashalg = 'sha512'  # hashalg算法,用什么方式进行加密
                    )

    # 授权机制
    authz_policy = ACLAuthorizationPolicy()

    # config = Configurator(settings=settings)
    # config配置里添加权限
    config = Configurator(settings=settings, root_factory='myshop.RootFactory')
    # 添加认证机制到config
    config.set_authentication_policy(authn_policy)
    # 添加授权机制
    config.set_authorization_policy(authz_policy)

    config.add_renderer(".html", 'pyramid.mako_templating.renderer_factory')
    config.add_static_view('static', 'static', cache_max_age=3600)  # 静态资源
    # config.add_route('home', '/')  # url映射 对应home主页
    # config.add_route('category', '/category')  # url映射 对应category页面
    config.add_route('/', '/{ctrl}/{action}*pa')  # url映射 对应控制器-方法 页面
    config.add_route('index', '/{action:.*}')  # url映射 对应控制器-方法 页面
    config.scan()
    return config.make_wsgi_app()

 

RootFactory要做的事

查出user.id ---> 通过user查出组 user.group.id -> 保存起来 save it

查出组的权限 group.permission ---->  对比视图函数权限  view(permission=???)   检查成功则说明有这个权限,继续访问这个视图

RootFacory里就是要查出哪个组有哪个权限的信息,然后告诉pyramid这个框架

给item视图加权限

# -*- coding:UTF-8 -*-
from pyramid.response import Response
from pyramid.view import view_config, view_defaults
from myshop.lib import category
from base import CBase

ctrl = 'item'

# @view_config(route_name='home', renderer='templates/mytemplate.pt')
@view_defaults(route_name='/')
class item(CBase):
    def __init__(self, request):
        CBase.__init__(self, request)
        self.request.title = u'商品'

    @view_config(match_param=('ctrl=%s' % ctrl, 'action=view'),
                 renderer="item.html", permission='item')
                 # renderer="string")
    def view(self):
        id = self.request.params.get('id')  # 接收id
        item_info = category.get_item_by_id(id)
        return {'item':item_info}

登录有权限的账号发现

 

 如图:如果没有添加商品得权限则不让添加商品按钮显示

model.py中给user添加校验方法

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)  # primary_key 主键
    name = Column(Unicode(255), nullable=False, unique=True)  # nullable=False
                                                              # 不允许为空 unique 唯一
    password = Column(Unicode(255), nullable=False)
    email = Column(Unicode(255), unique=True)
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False)  # 关联外键groups的id
    group = relationship('Group', backref='users')
    # 可以使用User的实例对象+".group"查询Group的数据,\
    #  backref可以使用Group的实例对象查询User的数据,\
    # 以列表形式返回 相当于在Group里写users = relationship('User')

    def has_permission(self, permission):
        # import pdb;pdb.set_trace()
        for perm in self.group.permissions:
            if perm.name == permission:
                return True
        return False

然后再视图模板中调用该方法

<ul class="menu_r">
            <li><a href="${request.route_path('index', ctrl='', action='',pa=())}">首页</a></li>
            % if request.title==u'分类':
                % if request.user.has_permission('item'):
                    <li><a href="#"  onclick="itemAdd()">添加商品</a></li>
                % endif
            % endif
        </ul>

 

单步调试

在需要调试的地方引用pdb;pdb_set_trace()

此处__init.py中的groupfinder方法需要调试

def groupfinder(userid, request):
    """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
    import pdb;pdb.set_trace()
    user_info = user.get_user_by_id(userid)  # 查询登录账户信息
    if user_info:
        return [user_info.group.id]
    return None

登录账号然后查看终端

 

 此处可以打印下id等查看

也可以输入n进行下一步,一步一步调试

 

调试后发现返回的只是一个用户组的id,且id是整型 与RootFactory中__acl__定义的不一致

def groupfinder(userid, request):
    """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
    print("*" * 100)
    user_info = user.get_user_by_id(userid)  # 查询登录账户信息
    if user_info:
        return [user.group.id]  # 与__acl__定义的标识符不一致
    return None

class RootFactory(object):   # 创建RootFactory类
    def __init__(self, request):
        """读取所有权限"""
        group_list = user.get_group_list()  # 查询所有的组
        self.__acl__ = []  # 准备acl列表
        for group in group_list:
            for permission in group.permissions:
                # 给acl列表添加元祖,  1.Allow 允许或拒绝 需要导入Allow库 2.为了不和其它id冲突 添加g:的标识符 3.权限名称
                self.__acl__.append(
                    (Allow, 'g:' + str(group.id), permission.name)  
                )

修改为

def groupfinder(userid, request):
    """每当用户登录的时候,每当认证机制检查用户是否存在时都会调用下这个函数"""
    # import pdb; pdb.set_trace()  # 断点调试
    user_info = user.get_user_by_id(userid)  # 查询登录账户信息
    if user_info:
        return ['g:' + str(user_info.group.id)]  # 与__acl__定义的标识符保持一致
    return None

可以进入页面了

 登录时把user信息存入request

__init__.py配置config

def get_user(request):
    user_id = unauthenticated_userid(request)
    user_info = user.get_user_by_id(user_id)
    return user_info

def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    .
    .
    .
     # 添加授权机制
    config.set_authorization_policy(authz_policy)
    # 添加用户信息到request
    config.set_request_property(get_user,  # 回调函数
                                'user',   # 此处写user就是request.user 写u就是request.u
                                reify=True)  # 为True的时候会把登录用户保存下来 不需要每次区查询

 html模板里用request.user渲染数据

<span  class="fl">
         ${request.user.name},欢迎您的到来
</span>

 DBSession存入request

# 添加DBSession到request
    config.set_request_property(lambda request: DBSession,
                                'db',
                                reify=True)

用的时候需要传入request

def get_category_list(request):
    result = request.db.query(Category).filter_by(parent=None).all()
    return result

 ValueError: renderer was passed non-dictionary as value:渲染器以非字典形式作为值传递

做添加商品页面时出现错误

视图函数中item.py

 @view_config(match_param=('ctrl=%s' % ctrl, 'action=item_add'),
                 renderer="itemadd.html")
    def item_add(self):

        category_id = self.request.params.get('category_id','')
        print(category_id)
        return category_id

 

 解决方法-使用字典传值

 @view_config(match_param=('ctrl=%s' % ctrl, 'action=item_add'),
                 renderer="itemadd.html")
    def item_add(self):
        result = {}
        result['category_id'] = self.request.params.get('category_id', '')
        print(result)
        return result

 

posted on 2020-01-30 19:28  BZ易风  阅读(1511)  评论(0编辑  收藏  举报