2017-05-10 74 views
1

我有一个关于如何在启动模型实例时动态设置模型属性的问题。Django:基于JSONField动态设置模型实例属性

我使用了原生的PostgreSQL JSONField一个简单的模型:

from django.db import models 
from django.contrib.postgres.fields import JSONField 

class Booking(models.Model): 
    data = JSONField(blank=False, encoder=DjangoJSONEncoder) 

有什么办法来设置基于存储的“数据”字段中的值的模型实例属性当模型实例?

我希望能够做到这一点:

from .models import Booking 

b = Booking.objects.create(data={'foo':'bar'}) 
b.foo # this should yield 'bar' 

我最初的想法是要覆盖模型的初始化方法,并设置有SETATTR()的实例属性,但覆盖模型的初始化方法在Django文档中强烈建议不要使用它。

有没有其他办法可以达到这个目的?任何帮助表示赞赏。 PS:我知道我可以在模型实例中访问存储在'data'中的值,如:booking.data ['foo'],所以这不是我正在寻找的。

+0

听起来像自己绑起来节只是使用JSONField – e4c5

回答

0

你可以简单地实现在模型中__getattr__挂钩,即:

class Booking(models.Model): 

    # ... 
    def __getattr__(self, name): 
     try: 
      return self.data[name] 
     except KeyError: 
      try: 
       return super(Boooking, self).__getattr__(name) 
      except AttributeError: 
       raise AttributeError(
        "object %s has no attribute '%s'" % (type(self).__name__, name) 
        ) 

但除非你真的相信你的JSON内容将始终具有相同的结构,我会建议反对 - 使用简单的instance.data[key]语法更加明确。

+0

感谢所有。 __getattr__是我忽略的缺失部分。接受bruno desthuilliers的答案,因为错误处理更完整。顺便说一下,我同意:instance.data [key]更加明确,但在我的用例中,实例被传递给调用getattr(instance,'foo')的第三方类方法,并且重写此方法看起来不那么干净我。 –

0

如果您确实想要这样做,您可以覆盖getattr方法。

class Booking(models.Model): 

    def __getattr__(self, attr): 
     if attr in self.data: 
      return self.data[attr] 
     else: 
      return super(Booking, self).__getattr__(attr) 
+0

的目的实际上是'__getattr__'不'getattr',并且父类可以不定义它,因此'super'调用可能引发'AttributeError'。 –

+0

糟糕。错过我猜。非常感谢。 – hspandher