【问题标题】:How to store django objects as session variables ( object is not JSON serializable)?如何将 django 对象存储为会话变量(对象不是 JSON 可序列化的)?
【发布时间】:2020-02-12 02:47:16
【问题描述】:

我有一个简单的看法

def foo(request):
   card = Card.objects.latest(datetime)
   request.session['card']=card

对于上面的代码,我得到了错误

"<Card: Card object> is not JSON serializable"

Django 版本 1.6.2。我究竟做错了什么 ?

【问题讨论】:

    标签: python django


    【解决方案1】:

    在会话中,我只存储对象主键:

    request.session['card'] = card.id
    

    当从会话中加载卡片时,再次获取卡片:

    try:
        card = Card.objects.get(id=request.session['card'])
    except (KeyError, Card.DoesNotExist):
        card = None
    

    如果会话中没有card 条目或特定卡不存在,则将card 设置为None

    默认情况下,会话数据为serialised to JSON。您还可以provide your own serializer,它知道如何存储card.id 值或其他一些表示形式,并在反序列化时再次生成您的Card 实例。

    【讨论】:

    • 会话变量不是 cookie...(?)
    • @alias51:我认为我假设使用了基于 cookie 的会话。 2014 年太早了,无法确定,但在这里并不重要。
    【解决方案2】:

    有两种简单的方法可以做到这一点。

    • 如果每个对象同时属于一个会话,则将会话 ID 存储为模型字段,并更新模型。
    • 如果一个对象可以同时属于多个会话,请将 object.id 存储为会话变量。

    【讨论】:

      【解决方案3】:

      不幸的是,如果对象不是数据库对象而是某种其他类型的对象(例如 datetime 或对象类 Foo(object): pass that is not a database model object),则建议的答案不起作用。

      当然,如果对象碰巧有一些 id 字段,您可以将 id 字段存储在数据库中并从那里查找值,但通常它可能没有这么简单的值,唯一的方法是转换数据以您可以读取该字符串并根据字符串中的信息重建对象的方式进行字符串化。

      在 datetime 对象的情况下,由于简单的 datetime 对象可以通过简单地不打印任何内容来打印出格式 %Z,但如果没有任何内容,strptime 对象无法读取格式 %Z,这使情况变得更加复杂。除非那里有有效的时区规范,否则会阻塞 - 因此,如果您有一个可能包含或可能不包含 tzinfo 字段的日期时间对象,您真的必须使用 %Z 执行 strptime 两次,然后如果它在没有 %Z 的情况下阻塞。这很愚蠢。由于 datetime 对象具有 fromtimestamp 函数但没有 totimestamp 函数统一生成 fromtimestamp 将读取的时间戳,这一事实变得更加愚蠢。如果有一种格式代码会产生我一次又一次没有找到的时间戳编号,那么 strftime/strptime 会受到如上所述它们不是对称的事实的影响。

      【讨论】:

        【解决方案4】:

        @Martijn 是或可能是将对象保存在会话变量中的正确方法。

        但是这个问题通过回到 Django 1.5 得到了解决。所以这个问题是专门针对 django 1.6.2 的。

        希望这会有所帮助。

        【讨论】:

        • 您可以使用PickleSerializer,这是 Django 1.5 的默认值。您必须更改 SESSION_SERIALIZE 中的 settings.py
        • @akash-deshpande 你是如何在会话变量中存储对象的?
        • Django 做出这个改变有一个很好的理由:因为使用pickles 有安全隐患。您通常不想将 pickle 用于会话数据。
        【解决方案5】:

        对象不能存储在 Django 1.6 或更高版本的会话中。 如果您不想更改 cookie 值(对象)的行为,您可以在此处添加字典。这可以用于非数据库对象,如类对象等。

        from django.core.serializers.json import DjangoJSONEncoder
        import json     
        card_dict = card.__dict__
        card_dict .pop('_state', None) #Pop which are not json serialize
        card_dict = json.dumps(card_dict , cls=DjangoJSONEncoder)
        request.session['card'] = card_dict 
        

        希望对你有帮助!

        【讨论】:

        • 如果您提供自定义序列化程序,它们可以。不要手动执行此操作,而是配置 SESSION_SERIALIZER
        【解决方案6】:

        -->永远不要做这样的事情!
        Django 使用 sessionctypes:请阅读下面的Martijn Pieters 解释评论。

        class MyObject:
          def stuff(self):
            return "stuff..."
        
        my_obj = MyObject()
        request.session['id_my_obj'] = id(my_obj)
        
        ...
        
        id_my_obj = request.session.get('id_my_obj')
        
        import ctypes
        obj = ctypes.cast(id_my_obj, ctypes.py_object).value
        
        print(obj.stuff())    
        # returns "stuff..."
        

        【讨论】:

        • 永远不要这样做。这是一个非常糟糕的主意。如果您在多进程或多机设置中托管 Django,或者让 my_obj 被垃圾收集,这不仅不起作用,而且导致内存损坏。 id(my_obj) 可以轻松地 指向已经被其他东西使用的任意一块内存,但是在使用ctypes.cast().value 的那一刻,Python 将尝试更新该“对象”的引用计数,因此开始写入那块内存。
        猜你喜欢
        • 2013-05-23
        • 2015-02-24
        • 2014-10-23
        • 2018-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-21
        • 1970-01-01
        相关资源
        最近更新 更多