alukach
alukach

Reputation: 6298

Django HStore: How to override a key-value model field's __getattr__ and __setattr__

I've been getting my feet wet with using hstore in Django, through the django-hstore module. A big advantage to using hstore is that it allows for key-values to be stored in a field while providing decent indexing in Postgresql. A big disadvantage to django-hstore (and hstore in general) is that you can only store values as strings.

To overcome this, I thought it would be nice to override the model field (hstore.DictionaryField) so that any data entered in to the field is automatically encoded in JSON and any data retrieved is automatically decoded from JSON. I've attempted to do this by overriding the __setattr__ method but this causes tons of errors (when all the properties of the field are being set). Any ideas of the right way to do this?

What I have so far (I've commented out the getter portions while focusing on the setter, but left it in to show what I had in mind):

import simplejson as json

from django.db import models
from django_hstore import hstore


def _unpack_json(value):
    try:
        value = json.loads(value)
    except TypeError:
        pass
    return value


class HStoreJsonDict(hstore.DictionaryField):

    @staticmethod
    def _load_json(value):
        try:
            value = json.dumps(value)
        except Exception as e:
            # Is this needed?
            print(value, e)
            pass
        return value

    # def __getattribute__(self, key):
    #     print('__getattribute__')
    #     value = super(HStoreJsonDict, self).__getattribute__(key)
    #     return _unpack_json(value)

    # def __getitem__(self, key):
    #     print('__getitem__')
    #     value = super(HStoreJsonDict, self).__getitem__(key)
    #     return _unpack_json(value)

    def __setattr__(self, key, value):
        print('__setattr__', key, value)
        value = self._load_json(value)
        return super(HStoreJsonDict, self).__setattr__(key, value)

class TestModel(models.Model):
    name = models.CharField(max_length=64)
    data = HStoreJsonDict(db_index=True)
    objects = hstore.HStoreManager()

    def __unicode__(self):
        return '%s - %s' % (self.name, self.data)

Upvotes: 2

Views: 1263

Answers (1)

alukach
alukach

Reputation: 6298

At the end of the day, I found it easiest to fork the django-hstore module and to put serialization/deserialization in the DictionaryField's get_prep_value() and to_python() methods (see here for code).

For anyone else looking to manipulate data on entry-to/retrieval-from the database when using Django, I highly recommend checking Django's docs on to_python() and get_prep_value().

Upvotes: 1

Related Questions