pyramid学习笔记3-创建注册页面
Pyramid可以为资源、路由、静态资源产生URL,pyramid有一个统一的view callables概念,view callables可以是函数、类的方法,或者一个实例。顾名思义,view callables就是让这个view callable处理对应的view URL页面。在mypriject->view->views.py添加/user/regist注册view callables:
#coding=utf-8 from pyramid.view import view_defaults,view_config from myproject.api.validator import * from myproject.api.simpleform import Form,State from logging import getLogger log = getLogger(__name__) def includeme(config): config.scan(__name__) config.add_route('user', '/user/{action}') config.add_route('event', '/event/{action}') @view_defaults(route_name='user') class UserView(object): def __init__(self, request): self.request = request self.db=request.db @view_config(renderer='user/regist.mako', match_param=('action=regist'), request_method='GET') @view_config(renderer='jsonp', match_param=('action=regist'), request_method='POST') def regist(self): if self.request.method=="POST": validators = dict( phone = PhoneNumber(), name = RealName(), password = Password() ) form = Form(self.request, validators=validators, state=State(request=self.request), variable_decode=True) if form.validate(force_validate=True): log.debug(form.data) if self.db.user.find_one({'phone':form.data.get('phone')}): return dict(error='该号码已经注册') if form.data.get('name')=='习近平': return dict(error='姓名中不能有敏感词汇') self.db.user.save({'phone':form.data.get('phone'), 'name':form.data.get('name'), 'password':form.data.get('password')}) return {} @view_defaults(route_name='event') class EventView(object): def __init__(self, request): self.request = request @view_config(renderer='event/create.mako', match_param=('action=create'), request_method='GET') def create(self): return {}以上用到了pyramid.config和pyramid.view模块,详细说明参考官方文档:
https://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/latexindex.html
logging模块用于开发调试用,myproject.api.simpleform和validator用于前台数据验证,这里我们自己封装了api。在myproject下新建api包:创建__init__.py,validator.py和simpleform.py文件:
validator.py
#coding=utf-8 import formencode as fe import re from bson.objectid import ObjectId _ = lambda s: s class PhoneNumber(fe.FancyValidator): def _convert_to_python(self, value, state): validator = fe.validators.Regex(r'^(13|15|18)\d{9}$') value = validator.to_python(value, state) return value _to_python = _convert_to_python def Password(*kw, **kwargs): return fe.validators.String(min=6, max=20) def RealName(*kw, **kwargs): return fe.validators.UnicodeString(min=2, max=10)这里用到了formcode模块,详细参考官方网站:
https://www.formencode.org/en/latest/
由于MongoDB是采用bson的形式存储数据的,所以引入bson模块的ObjectId对文档的id进行转换。
simpleform.py
#coding=utf-8 import warnings from formencode import htmlfill from formencode import variabledecode from formencode import Invalid from formencode.api import NoDefault from pyramid.i18n import get_localizer, TranslationStringFactory, TranslationString from pyramid.renderers import render class State(object): """ Default "empty" state object. Keyword arguments are automatically bound to properties, for example:: obj = State(foo="bar") obj.foo == "bar" """ def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) def __contains__(self, k): return hasattr(self, k) def __getitem__(self, k): try: return getattr(self, k) except AttributeError: raise KeyError def __setitem__(self, k, v): setattr(self, k, v) def get(self, k, default=None): return getattr(self, k, default) fe_tsf = TranslationStringFactory('FormEncode') def get_default_translate_fn(request): pyramid_translate = get_localizer(request).translate def translate(s): if not isinstance(s, TranslationString): s = fe_tsf(s) return pyramid_translate(s) return translate class Form(object): """ Legacy class for validating FormEncode schemas and validators. :deprecated: 0.7 `request` : Pyramid request instance `schema` : FormEncode Schema class or instance `validators` : a dict of FormEncode validators i.e. { field : validator } `defaults` : a dict of default values `obj` : instance of an object (e.g. SQLAlchemy model) `state` : state passed to FormEncode validators. `method` : HTTP method `variable_decode` : will decode dict/lists `dict_char` : variabledecode dict char `list_char` : variabledecode list char Also note that values of ``obj`` supercede those of ``defaults``. Only fields specified in your schema or validators will be taken from the object. """ default_state = State def __init__(self, request, schema=None, validators=None, defaults=None, obj=None, extra=None, include=None, exclude=None, state=None, method="POST", variable_decode=False, dict_char=".", list_char="-", multipart=False, ignore_key_missing=False, filter_extra_fields=True): self.request = request self.schema = schema self.validators = validators or {} self.method = method self.variable_decode = variable_decode self.dict_char = dict_char self.list_char = list_char self.multipart = multipart self.state = state self.ignore_key_missing = ignore_key_missing self.filter_extra_fields = filter_extra_fields self.is_validated = False self.errors = {} self.data = {} if self.state is None: self.state = self.default_state() if not hasattr(self.state, '_'): self.state._ = get_default_translate_fn(request) if defaults: self.data.update(defaults) if obj: fields = self.schema.fields.keys() + self.validators.keys() for f in fields: if hasattr(obj, f): self.data[f] = getattr(obj, f) def is_error(self, field): """ Checks if individual field has errors. """ return field in self.errors def all_errors(self): """ Returns all errors in a single list. """ if isinstance(self.errors, basestring): return [self.errors] if isinstance(self.errors, list): return self.errors errors = [] for field in self.errors.iterkeys(): errors += self.errors_for(field) return errors def errors_for(self, field): """ Returns any errors for a given field as a list. """ errors = self.errors.get(field, []) if isinstance(errors, basestring): errors = [errors] return errors def validate(self, force_validate=False, params=None): """ Runs validation and returns True/False whether form is valid. This will check if the form should be validated (i.e. the request method matches) and the schema/validators validate. Validation will only be run once; subsequent calls to validate() will have no effect, i.e. will just return the original result. The errors and data values will be updated accordingly. `force_validate` : will run validation regardless of request method. `params` : dict or MultiDict of params. By default will use **request.POST** (if HTTP POST) or **request.params**. """ assert self.schema or self.validators, \ "validators and/or schema required" if self.is_validated: return not(self.errors) if not force_validate: if self.method and self.method != self.request.method: return False if params is None: if not force_validate and self.method == "POST": params = self.request.POST else: params = self.request.params if self.variable_decode: decoded = variabledecode.variable_decode( params, self.dict_char, self.list_char) else: decoded = params self.data.update(decoded) if self.schema: self.schema.ignore_key_missing = self.ignore_key_missing try: self.data = self.schema.to_python(decoded, self.state) except Invalid, e: self.errors = e.unpack_errors(self.variable_decode, self.dict_char, self.list_char) if self.validators: for field, validator in self.validators.iteritems(): value = decoded.get(field) if value is None: try: if_missing = validator.if_missing except AttributeError: if_missing = NoDefault if if_missing is NoDefault: if self.ignore_key_missing: continue try: message = validator.message('missing', self.state) except KeyError: message = self.state._('Missing value') self.errors[field] = unicode(message) else: value = if_missing try: self.data[field] = validator.to_python(value, self.state) except Invalid, e: self.errors[field] = unicode(e) self.is_validated = True return not(self.errors) def bind(self, obj, include=None, exclude=['access_token']): """ Binds validated field values to an object instance, for example a SQLAlchemy model instance. `include` : list of included fields. If field not in this list it will not be bound to this object. `exclude` : list of excluded fields. If field is in this list it will not be bound to the object. Returns the `obj` passed in. Note that any properties starting with underscore "_" are ignored regardless of ``include`` and ``exclude``. If you need to set these do so manually from the ``data`` property of the form instance. Calling bind() before running validate() will result in a RuntimeError """ if not self.is_validated: raise RuntimeError, \ "Form has not been validated. Call validate() first" if self.errors: raise RuntimeError, "Cannot bind to object if form has errors" items = [(k, v) for k, v in self.data.items() if not k.startswith("_")] fields = [] if self.schema: fields = fields + self.schema.fields.keys() if self.validators: fields = fields + self.validators.keys() for k, v in items: if self.filter_extra_fields and fields and k not in fields: continue if include and k not in include: continue if exclude and k in exclude: continue if isinstance(obj, dict): obj.update({k:v}) else: setattr(obj, k, v) return obj def htmlfill(self, content, **htmlfill_kwargs): """ Runs FormEncode **htmlfill** on content. """ charset = getattr(self.request, 'charset', 'utf-8') htmlfill_kwargs.setdefault('encoding', charset) return htmlfill.render(content, defaults=self.data, errors=self.errors, **htmlfill_kwargs) def render(self, template, extra_info=None, htmlfill=True, **htmlfill_kwargs): """ Renders the form directly to a template, using Pyramid's **render** function. `template` : name of template `extra_info` : dict of extra data to pass to template `htmlfill` : run htmlfill on the result. By default the form itself will be passed in as `form`. htmlfill is automatically run on the result of render if `htmlfill` is **True**. This is useful if you want to use htmlfill on a form, but still return a dict from a view. For example:: @view_config(name='submit', request_method='POST') def submit(request): form = Form(request, MySchema) if form.validate(): # do something return dict(form=form.render("my_form.html")) """ extra_info = extra_info or {} extra_info.setdefault('form', self) result = render(template, extra_info, self.request) if htmlfill: result = self.htmlfill(result, **htmlfill_kwargs) return result
然后修改myproject下的__init__.py(不是view下的__init__.py):
from pyramid.config import Configurator from pyramid.renderers import JSONP from pyramid.security import unauthenticated_userid from bson.objectid import ObjectId from bson.dbref import DBRef from urlparse import urlparse import pymongo, datetime def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ #using config config = Configurator(settings=settings) #add static view config.add_static_view('static', 'static', cache_max_age=3600) #add jsonp jsonp = JSONP(param_name='callback') jsonp.add_adapter(ObjectId, objectid_adapter) jsonp.add_adapter(DBRef, dbref_adapter) config.add_renderer('jsonp', jsonp) #add request property config.set_request_property(get_db, "db", reify=True) #config.set_request_property(get_user, "user", reify=True) config.include("myproject.view.views") config.scan() return config.make_wsgi_app() def objectid_adapter(obj, request): return str(obj) def dbref_adapter(obj, request): return {'$id': obj.id} ''' def get_user(request): userid = unauthenticated_userid(request) if userid is not None and ObjectId.is_valid(userid): user = request.db.user.find_one({'_id': ObjectId(userid)}) if user and user.get('status') == 2: return User(user) ''' def get_db(request): settings = request.registry.settings db_url = urlparse(settings['mongo_uri']) conn = pymongo.MongoClient(host=db_url.hostname, port=db_url.port) db = conn[db_url.path[1:]] if db_url.username and db_url.password: db.authenticate(db_url.username, db_url.password) return db
为mako创建通用模板templates/template.mako:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="https://www.w3.org/1999/xhtml" xml:lang="zh-cn" xmlns:tal="https://xml.zope.org/namespaces/tal" > <head> <title>时刻</title> <meta charset="utf-8"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="" /> <meta name="description" content="" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://yui.yahooapis.com/pure/0.4.2/pure-min.css"> <link rel="stylesheet" type="text/css" href="/static/css/style.css"> <!--[if lt IE 9]> <script src="https://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script> <![endif]--> <script type="text/javascript" src="/static/js/jquery-1.10.1.min.js"></script> </head> <body> ${next.body()} </body> </html>
修改regist.mako文件:
<%inherit /> <div > <h1>注册</h1> </div> <div > <form method="post"> <label>手机</label> <input name="phone" type="text" placeholder="请输入您的手机号码"> <label>姓名</label> <input name="name" type="text" placeholder="请输入您的姓名"> <label>密码</label> <input name="password" type="password" placeholder="请输入密码"> <a >注册</a> <a >返回</a> </form> </div> <script> $(document).ready(function(){ $('#regist-btn').click(function(){ if($('#phone').val()==""){ alert('请输入手机号码'); } else if($('#password').val()==""){ alert('请输入密码'); } else if($('#name').val()==""){ alert('请输入姓名'); } else{ $.ajax({ url:'/user/regist', type:'POST', dataType:'json', data:{ 'phone':$('#phone').val(), 'password':$('#password').val(), 'name':$('#name').val() }, success:function(data){ if(data.error){ if(data.form_errors && data.form_errors.phone) alert(data.form_errors.phone); else if(data.form_errors && data.form_errors.password) alert(data.form_errors.password); else if(data.form_errors && data.form_errors.name) alert('姓名:'+data.form_errors.name); else alert(data.error); } else if(data.result==1){ window.location.href="/share/event?id=533bd7c3fbe78e78841aa359"; } else{ } }, error:function(data){ console.log('系统错误!'); } }); } }); }); </script>
最后运行项目,一个完整地注册流程就ok了。运行前需要先开启MongoDB,并且确定创建了myproject数据库和相应集合。
源代码:
https://pan.baidu.com/s/15ZLBC
最后更新:2017-04-03 12:56:11