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