Search code examples
pythondjangopostgresqldjango-jsonfield

Django: Dynamically set model instance attributes based on JSONField


I have a question on how to set model attributes dynamically when model instances are initiated.

I am using a simple model with a native PostgreSQL JSONField:

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

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

Is there any way to set attributes on the model instance based on the values stored in the 'data' field when the model is instantiated?

I want to be able to do this:

from .models import Booking

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

My initial thought was to override the model's init method and set the instance attributes with setattr(), but overriding a model's init method is strongly discouraged in the Django docs.

Is there any other way to achieve this? Any help is appreciated.

PS: I know that I can access the values stored in 'data' on a model instance like so: booking.data['foo'], so this is not what I am looking for.


Solution

  • You could simply implement the __getattr__ hook in your model, ie:

    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)
                       ) 
    

    but unless you are really confident that your json content will always have the same structure I would advise against it - using the plain instance.data[key] syntax is more explicit.