模板
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