有件事困扰着我,我正在学习django(cs50)的初学者教程,在某个时候我们收到了一个表单提交返回的字符串,并想把它添加到列表中:https://www.youtube.com/watch?v=w8q0C-C1js4&list=PLhQjrBD2T380xvFSUmToMMzERZ3qB5Ueu&t=5777s
def add(request):
if 'tasklist' not in request.session:
request.session['tasklist'] = []
if request.method == 'POST':
form_data = NewTaskForm(request.POST)
if form_data.is_valid():
task = form_data.cleaned_data['task']
request.session['tasklist'] += [task]
return HttpResponseRedirect(reverse('tasks:index'))
我检查了request.session['tasklist']
的类型,python显示它是一个列表,task
变量是一个字符串。
那么为什么request.session['tasklist'].append(task)
不能正常工作呢?我可以看到它通过一些print语句被添加到列表中,但是它又被"忘记了"--它似乎没有被永久添加到任务列表中。
为什么我们要使用request.session['tasklist'] += [task]
?
我唯一能找到的是https://ogirardot.wordpress.com/2010/09/17/append-objects-in-request-session-in-django/,但那指的是一个已经不存在的网站。
代码运行良好,但我试图理解为什么您需要使用不同的操作,并且不能/不应该使用append方法。
谢谢。
3条答案
按热度按时间2lpgd9681#
它不起作用的原因是因为django没有看到您在会话中的列表上使用
append()
方法更改了会话中的任何内容。您在这里所做的实际上是提取对列表的引用并对其进行更改,而会话后端对此一无所知。
append()
方法在列表本身上,而不在会话对象上append()
时,您只是在与列表对话,而列表的父进程(会话)并不知道您在做什么session['whatever'] = 'something'
上执行分配时,它会知道发生了一些事情并进行了更改Django认为只有当会话项被重新分配给会话时,它才需要保存更改的会话项。包含
self.modified = True
语句的__setitem__
方法。session['list'] += [new_element]
添加新列表项(改变存储在会话中的列表,因此列表引用保持不变),然后再次将其重新分配给会话-〉从而首先触发__getitem__
调用-〉然后您的+= / __iadd__
在读取的值上运行-〉然后进行__setitem__
调用(传递了list ref.).你可以在django代码库中看到,它在每次__setitem__
调用后将会话标记为已修改。session['list'] = session['list'] + [new_item]
模式做同样的事情,每次运行时都会创建一个新列表,所以效率有点低,但是无论如何你不应该在会话中存储数百个条目,所以你可能会没事,这也和上面的工作原理完全一样。但是,如果您在会话中使用子密钥(如
session['list']['x'] = 'whatever'
),则会话将不会看到自己已修改,因此您需要将其标记为byrequest.session.modified = True
pbgvytdp2#
简短回答:它是关于Python如何选择实现dict数据结构的。
长回答:
让我们先说request.session是一个字典。
引用Django的文档,"默认情况下,Django只在会话被修改时才保存到会话数据库中-也就是说,如果它的任何字典值被赋值或删除"。链接
因此,问题是会话数据库没有被request. session ['tasklist ']. append(task)修改
查看Django的Session base code的相关部分(如@Csaba K.在回答中所发布的),当调用setitemdunder方法时,变量self. modified将被设置为True。
现在,在这一步,问题似乎是setitem dunder方法不是用request. session ['tasklist ']. append(task)调用的,而是用request. session ['tasklist']+=[task]调用的。这不是由于request. session ['tasklist ']的引用是否发生了变化,而是因为对底层列表的引用保持不变。
为了确认,让我们创建一个扩展Python dict的自定义字典,并在调用setitemdunder方法时打印一些内容。
它打印:
正如我们所看到的,setitemdunder方法被调用,而self.modified只有在添加新的键值对时,或者使用+=或+时才被设置为true,而在初始化、追加或扩展可迭代对象时则不被设置为true(在这里是一个列表)。现在,运算符+和+=在Python中的作用非常不同,正如在另一个答案中所解释的那样,+=的行为更像append方法,但在这个例子中,我想它更多地是关于Python如何选择实现dict数据结构,而不是+,+=和append在列表上的行为。
cngwdvgl3#
我在做更多搜索时发现了这个:https://code.djangoproject.com/wiki/NewbieMistakes
滚动到“追加到会话中的列表不起作用”
同样,这是一个非常过时的条目,但似乎仍然成立。
不完全满意,因为这并没有回答“为什么”这不起作用的问题,但至少证实了“发生了一些事情”,你可能仍然应该使用那里的建议。
(if实际上,任何人都可以用更详细的方式来解释这一点,那么我很乐意听到它)