python 追加到request.sessions[列表] in Django

p5cysglq  于 2023-01-01  发布在  Python
关注(0)|答案(3)|浏览(113)

有件事困扰着我,我正在学习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方法。
谢谢。

2lpgd968

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'),则会话将不会看到自己已修改,因此您需要将其标记为by request.session.modified = True

pbgvytdp

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方法时打印一些内容。

class MyDict(dict):
    def __init__(self, globalVar):
        super().__init__()
        self.globalVar = globalVar

    def __setitem__(self, key, value):
        super().__setitem__(key, value)
        print("Called Set item when: ", end="")

myDict =  MyDict(0)
print("Creating Dict")

print("-----")

myDict["y"] = []
print("Adding a new key-value pair")

print("-----")

myDict["y"] += ["x"]
print(" using +=")

print("-----")

myDict["y"].append("x")
print("append")

print("-----")

myDict["y"].extend(["x"])
print("extend")

print("-----")

myDict["y"] = myDict["y"] + ["x"]
print(" using +",)

print("-----")

它打印:

Creating Dict
-----
Called Set item when: Adding a new key-value pair
-----
Called Set item when:  using +=
-----
append
-----
extend
-----
Called Set item when:  using +
-----

正如我们所看到的,setitemdunder方法被调用,而self.modified只有在添加新的键值对时,或者使用+=或+时才被设置为true,而在初始化、追加或扩展可迭代对象时则不被设置为true(在这里是一个列表)。现在,运算符+和+=在Python中的作用非常不同,正如在另一个答案中所解释的那样,+=的行为更像append方法,但在这个例子中,我想它更多地是关于Python如何选择实现dict数据结构,而不是+,+=和append在列表上的行为。

cngwdvgl

cngwdvgl3#

我在做更多搜索时发现了这个:https://code.djangoproject.com/wiki/NewbieMistakes
滚动到“追加到会话中的列表不起作用”
同样,这是一个非常过时的条目,但似乎仍然成立。
不完全满意,因为这并没有回答“为什么”这不起作用的问题,但至少证实了“发生了一些事情”,你可能仍然应该使用那里的建议。
(if实际上,任何人都可以用更详细的方式来解释这一点,那么我很乐意听到它)

相关问题