Django:在同一个视图中设置会话并获取会话密钥

26

我想将一些东西存储在数据库中,并且正在使用当前会话作为外键:

来自models.py

class Visited(models.Model):
    session = models.ForeignKey(Session)
    page = models.ForeignKey(Page)
    times_visited = models.PositiveIntegerField()
    ip_address = models.IPAddressField()
    date_last_visited = models.DateTimeField()
    def __unicode__(self):
        return u'(%s, %s)' % (str(self.session.session_key), str(self.page.name))

为了为这个模型创建新的条目,我正在使用以下代码来获取当前会话(在views.py中):

Session.objects.get(session_key=request.session.session_key)
然而,如果用户第一次访问网站,因此还没有设置cookie,上面的代码将会产生一个“DoesNotExist”错误。
我知道即使没有设置cookie,您仍然可以设置session对象。因此,我可以想到一些方法来解决这个问题,例如:
- 将唯一标识符设置为会话对象(除了会话密钥)。 - 暂时将要添加到数据库的数据存储为会话对象,并使用装饰器函数在使用会话之前检查其是否存在。 - 仅使用会话对象,不将任何内容存储在数据库中(从技术上讲,这是可能的,但对于我的实现,它取决于Python字典(带有几百个条目)是否至少与数据库一样有效,例如用于排序)。
但我希望有一个更好的解决方案。是否有任何通常使用或良好的解决方案来解决这个问题?或者我在我的模型中参考会话方式是否正确?
谢谢您的帮助。
2个回答

58

request.session 是一个 SessionStore 对象,具有唯一的 session_key。

只要访问该属性,session_key 就会被创建。但是 session 对象本身只在视图被处理后(通过调用 SessionStore 对象的 save 方法,在会话中间件的 process_response 方法中)保存到数据库中。

虽然没有详细的文档说明,但从源代码来看,你应该像这样创建一个新的 session 对象:

if not request.session.exists(request.session.session_key):
    request.session.create() 

你也可以创建自定义的会话中间件,以确保在任何视图尝试访问它之前始终可用新的会话对象:

from django.conf import settings
from django.contrib.sessions.middleware import SessionMiddleware

class CustomSessionMiddleware(SessionMiddleware):
    def process_request(self, request):
        engine = import_module(settings.SESSION_ENGINE)
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
        request.session = engine.SessionStore(session_key)
        if not request.session.exists(request.session.session_key):
            request.session.create() 
(当然你必须通过在settings.py中引用SESSION_ENGINE来引用你的新会话中间件。)但是请注意,如果用户的浏览器不支持 cookie,这种方法将为每个请求生成一个新的会话对象...

谢谢你对它如何运作的出色解释(更确切地说是为什么它不起作用)。出于简单而非优雅的考虑,我选择了我提到的第二个“hack”的变体。 - Jon Cox
我对Django或Python不是太熟悉,但我相信这个答案正是我所需要的。如果从客户端传递了会话ID(别问为什么),我需要交换会话。但我有点困惑,我是否需要在MIDDLEWARE_CLASSES中引用此类,除了'django.contrib.sessions.middleware.SessionMiddleware'和SESSION_ENGINE之外?在SESSION_ENGINE中引用它肯定会导致这行代码递归,难道我错了吗? - Steve
我认为request.session.create()不会设置Set-Cookie头。 - Flimm
我不得不在会话中设置某些东西,如request.session['foo'] = 'bar',以防止SessionMiddleWare运行delete_cookie - Flimm
这非常有帮助。今天我简直不敢相信 Django 文档中没有提到这一点。清除用户历史记录后再创建新的历史记录似乎是一个很常见的事件。 - Ezekiel Kruglick
非常好的解决方案。但是一旦用户登录,就会出现冲突。在我的情况下,一切都很顺利,值得点赞。但很快,我的CMS工具栏消失了。所以我进行了调查,逐步撤销代码。一旦禁用中间件,它就可以正常工作。我怀疑问题在于这个中间件创建了一个会话密钥,这就是它的目的。但是一旦用户登录,另一个会话密钥就会生成,从而导致冲突。 - Peter

1
如果您想销毁会话,我建议先使用Django shell更好的方法。
from django.contrib.sessions.models import Session
Session.objects.all() #you see all sessions
Session.objects.all().delete() 

在上一个查询中,您可以根据需要进行筛选,并执行查询。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接