我根本无法理解这种行为。我在昨天或前一天问了一个问题,之前认为这是关于bottle.py的问题,但在尝试了各种可能的解决方案之后,甚至将我的应用程序转换为烧瓶,我已经将行为精确定位到一个非常简单的类,但是我不知道为什么会这样。这让我感到困惑,但如果任何人能够对此有所了解,我真的很想理解这一点。
好的,首先我有一个名为Page的类,它简化了一些模板的设置,这是令人讨厌的类:
class Page:
"""The page object constructs the webpage and holds associated variables and templates"""
def __init__(self, template=None, name = '', title='',template_vars={}, stylesheets=[], javascript=[]):
# Accepts template with or without .html extension, but must be added for lookup
self.stylesheet_dir = '/css'
self.javascript_dir = '/js'
self.template = template
self.template_vars = {}
self.name = name
self.title = title
self.stylesheets = stylesheets
self.javascript = javascript
self.template_vars['debug'] = _Config.debug
self.template_vars['title'] = self.title
self.template_vars['stylesheets'] = self.stylesheets
self.template_vars['javascript'] = self.javascript
def render(self):
"""Should be called after the page has been constructed to render the template"""
if not self.template.endswith(_Config.template_extension):
self.template += '.' + _Config.template_extension
if not self.title:
if self.name:
self.title = _Config.website + _Config.title_delimiter + self.name
else:
# If title not given use template name
self.title = _Config.website + _Config.title_delimiter + self.template.rstrip('.html')
try:
template = env.get_template(self.template)
except AttributeError:
raise (AttributeError, 'template not set')
rendered_page = template.render(self.template_vars)
return rendered_page
def add_stylesheet(self, name, directory=None):
# Sanitize name
if not name.endswith('.css'):
name += '.css'
if name.startswith('/'):
name = name.lstrip('/')
if not directory:
directory = self.stylesheet_dir
self.template_vars['stylesheets'].append(directory + '/' + name)
def add_javascript(self, name, directory=None):
# Sanitize name
if not name.endswith('.js'):
name += '.js'
if name.startswith('/'):
name = name.lstrip('/')
if not directory:
directory = self.javascript_dir
self.template_vars['javascript'].append(directory + '/' + name)
这是一个展示问题的路线示例:
@route('/create_account', method=['GET','POST'])
def create_account():
dbsession = db.Session()
page = Page('create account')
page.add_javascript('create_account.js')
if request.method == 'GET':
page.template = 'create_account'
page.template_vars['status'] = None
document = page.render()
dbsession.close()
return document
elif request.method == 'POST':
# Omitted
问题在于Page.add_javascript()方法。下次我转到/ create_account页面时,它不是创建一个新的Page对象而是重用旧的。我知道这个,因为如果我去两次页面,我将在我返回的html文档中为create_account.js提供两个entires(模板只运行for循环并为该列表中传递的任何js文件创建一个标记)。如果我去3次,它将被列出3次,40次,40次等等。现在,如果我只是将其更改为使用初始化程序而不是add_javascript方法,则问题就会消失。
@route('/create_account', method=['GET','POST'])
def create_account():
dbsession = db.Session()
page = Page('create account', javascript=['create_account.js'])
if request.method == 'GET':
page.template = 'create_account'
page.template_vars['status'] = None
document = page.render()
dbsession.close()
return document
elif request.method == 'POST':
然而,我怀疑某些事情仍然是错误的,只是为了我自己的理智,我需要了解这里到底发生了什么。在同一页面对象上调用add_javascript方法两次的幕后可能会发生什么?在创建页面对象的 new 实例后,该方法立即被称为 ,它可能从哪里获取template_vars的旧内容?
答案 0 :(得分:0)
问题是您为Page.__init__
函数使用了可变的默认值。请参阅http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments。
所以你做在每个请求上获得一个新的Page
实例,但是重新使用包含你的javascript等的列表/词典。
用None
替换list / dict默认参数值,检查None
中的__init__
。