Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django 跨登录持久化会话变量_Django - Fatal编程技术网

Django 跨登录持久化会话变量

Django 跨登录持久化会话变量,django,Django,我想在会话变量中保存有关用户首选项的信息。如果用户在注销时选择了一个首选项,然后在以后登录,我希望在不需要重新选择的情况下保留该首选项 Django会话在cookie中维护会话密钥以跟踪用户会话。据我所知,当用户登录时,此密钥会更改 a) 这是否意味着登录时会删除所有会话变量,或者是否存在任何形式的转换 b) 如果无法在登录时保存首选项,手动设置cookie是否是继续操作的最佳方式?我想象这样一个场景: 注销时,在cookie中维护首选项 登录时,将首选项复制到会话变量并写入db(通过信号?)

我想在会话变量中保存有关用户首选项的信息。如果用户在注销时选择了一个首选项,然后在以后登录,我希望在不需要重新选择的情况下保留该首选项

Django会话在cookie中维护会话密钥以跟踪用户会话。据我所知,当用户登录时,此密钥会更改

a) 这是否意味着登录时会删除所有会话变量,或者是否存在任何形式的转换

b) 如果无法在登录时保存首选项,手动设置cookie是否是继续操作的最佳方式?我想象这样一个场景:

  • 注销时,在cookie中维护首选项
  • 登录时,将首选项复制到会话变量并写入db(通过信号?)
  • 注销时,使用首选项更新Cookie(通过信号?)
更新


我通过将首选项保存在用户的profile对象以及cookie中(这些首选项在任何方面都不敏感),成功地获得了这个功能。当用户登录时,他们的配置文件设置优先。未登录时,当您登录/注销时选择cookie首选项。如果其他用户登录,Django将刷新所有会话(auth/init.py中的request.session.flush())


您最好将用户设置存储在数据库中,并添加一些中间件来获取数据并将其存储在您的请求中。

持续存在的用户数据听起来应该存在于类似模型的环境中。

我实际上认为您最初的设计是合理的。如果要跨登录/注销边界保存一些会话变量,可以执行以下操作

from functools import wraps

class persist_session_vars(object):
    """ Some views, such as login and logout, will reset all session state.
    However, we occasionally want to persist some of those session variables.
    """

    session_backup = {}

    def __init__(self, vars):
        self.vars = vars

    def __enter__(self):
        for var in self.vars:
            self.session_backup[var] = self.request.session.get(var)

    def __exit__(self, exc_type, exc_value, traceback):
        for var in self.vars:
            self.request.session[var] = self.session_backup.get(var)

    def __call__(self, test_func, *args, **kwargs):

        @wraps(test_func)
        def inner(*args, **kwargs):
            if not args:
                raise Exception('Must decorate a view, ie a function taking request as the first parameter')
            self.request = args[0]
            with self:
                return test_func(*args, **kwargs)

        return inner
无论从哪个视图调用auth.login/logout,都会抛出此装饰程序。如果您要将它们委托给内置视图,则可以轻松地包装它们

from django.contrib.auth import views

@persist_session_vars(['HTTP_REFERER'])
def login(request, *args, **kwargs):
    return views.login(request, *args, **kwargs)

您可以创建一个表单Mixin,该表单Mixin允许您将表单值持久化到用户的会话(不需要登录)。这对于公共表视图报表上的筛选/排序选项等内容非常有用,因为您希望在刷新期间保持其筛选选项的持久性

视图:

表格:


登录时,Django调用
session.flush()
session.cycle\u key()
,以确保旧会话中没有任何内容保留。这是一种安全措施,可防止会话固定漏洞。因此,在应用此解决方案时,请注意要保留哪些变量

如果您想保持某种状态,则必须在发布登录后恢复该状态

Chase Seibert的解决方案是一个很好的开端,由于代码中的线程安全问题,它非常不安全。您可以在此处找到安全使用的改进版本:

from functools import wraps

class persist_session_vars(object):
    """
    Some views, such as login and logout, will reset all session state.
    (via a call to ``request.session.cycle_key()`` or ``session.flush()``).
    That is a security measure to mitigate session fixation vulnerabilities.

    By applying this decorator, some values are retained.
    Be very aware what find of variables you want to persist.
    """

    def __init__(self, vars):
        self.vars = vars

    def __call__(self, view_func):

        @wraps(view_func)
        def inner(request, *args, **kwargs):
            # Backup first
            session_backup = {}
            for var in self.vars:
                try:
                    session_backup[var] = request.session[var]
                except KeyError:
                    pass

            # Call the original view
            response = view_func(request, *args, **kwargs)

            # Restore variables in the new session
            for var, value in session_backup.items():
                request.session[var] = value

            return response

        return inner
现在你可以写:

from django.contrib.auth import views

@persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
    return views.login(request, *args, **kwargs)
对于基于类的视图(django allauth):

并在url模式中使用该视图:

import allauth.urls
from django.conf.urls import include, url

from . import views

urlpatterns = [
    # Views that overlap the default:
    url(r'^login/$', views.LoginView.as_view(), name='account_login'),

    # default allauth urls
    url(r'', include(allauth.urls)),
]

一旦用户登录,它将被写入数据库,但是如果用户没有登录,我仍然希望他们能够设置首选项,一旦他们最终登录,这些首选项将写入userprofile模型,因此问题在于在登录时维护数据,但您无法知道是同一个人还是同一浏览器的另一个用户。因此,您很高兴假设这是同一个人?对于简单的显示设置,是的,但设置数据库对象可以标记为
已授权
,这样只有某些设置写入cookie,而所有设置都写入登录会话。这是一个很好的开始,但不是线程安全的!由于
persist\u session\u vars
只实例化一次,因此当两个用户同时登录时,所有用户都在向相同的
session\u backup
dict写入数据(即使将其设为对象变量而不是类属性,在这里也不会改变这一点),他们将收到彼此的会话变量。在您的示例中,您的统计信息将处于关闭状态。然而,以这种方式保存个人信息,会在网站上造成巨大的安全/信息泄漏。
from django.contrib.auth import views

@persist_session_vars(['some_field'])
def login(request, *args, **kwargs):
    return views.login(request, *args, **kwargs)
import allauth.account.views as auth_views
from django.utils.decorators import method_decorator

@method_decorator(persist_session_vars(['some_field']), name='dispatch')
class LoginView(auth_views.LoginView):
    pass
import allauth.urls
from django.conf.urls import include, url

from . import views

urlpatterns = [
    # Views that overlap the default:
    url(r'^login/$', views.LoginView.as_view(), name='account_login'),

    # default allauth urls
    url(r'', include(allauth.urls)),
]