Django: динамически устанавливать атрибуты экземпляра модели на основе JSONField.

У меня есть вопрос о том, как динамически устанавливать атрибуты модели при запуске экземпляров модели.

Я использую простую модель с собственным JSONField PostgreSQL:

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'

Моей первоначальной мыслью было переопределить метод init модели и установить атрибуты экземпляра с помощью setattr(), но переопределение метода init модели настоятельно не рекомендуется в документации Django.

Есть ли другой способ добиться этого? Любая помощь приветствуется.

PS: я знаю, что могу получить доступ к значениям, хранящимся в «данных» экземпляра модели, например так: booking.data['foo'], так что это не то, что я ищу.


person nikolas-berlin    schedule 10.05.2017    source источник
comment
Звучит как связывание себя узлами только ради использования JSONField   -  person e4c5    schedule 10.05.2017


Ответы (2)


Вы можете просто реализовать хук __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] более явно.

person bruno desthuilliers    schedule 10.05.2017
comment
Спасибо всем. getattr был недостающим элементом, который я упустил из виду. Принятие ответа Бруно Дестуилье, потому что обработка ошибок более полная. Кстати, я согласен: instance.data[key] является более явным, но в моем случае экземпляр передается методу стороннего класса, который вызывает getattr(instance, 'foo'), и переопределение этого метода кажется менее чистым для мне. - person nikolas-berlin; 10.05.2017

Если вы действительно хотите это сделать, вы можете переопределить метод 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)
person hspandher    schedule 10.05.2017
comment
на самом деле это __getattr__, а не getattr, и родительский класс может не определять его, поэтому вызов super может вызвать AttributeError. - person bruno desthuilliers; 10.05.2017
comment
Упс. Опечатка наверное. Большое спасибо. - person hspandher; 10.05.2017