2017-05-10 113 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