Recently we've been doing a lot of work with custom fields, and have
had a few inconveniences come up. Some minor, some not so much. I'd
like to propose a few changes to the API to make it easier to write
custom fields, especially those which use custom data structures.

The first of which, is the pre_save method. Originally we had been
using get_db_pre_value (which also is passed on to the save method),
and this seems to make a lot more sense than pre_save's
implementation. I'm not 100% sure which one is the "preferred" method,
but since examples are running around on the internet using both. It'd
make sense to iron out some of the details:

class ModifiedDateTimeField(DateTimeField):
    def pre_save(self, model_instance, add):
        value = datetime.datetime.now()
        setattr(model_instance, self.attname, value)
        return value

This is similar to an example found on the web, which gives you a
DateTimeField which automatically updates when the model is saved. The
only real issue I see with the pre_save method here, is that you're
still required to do setattr() in order to change the value, even if
you return a new value. Unless there is a specific reason for this, it
would make sense to have it automatically set the attribute on
pre_save. It seems, based on some examples, that this may be used
strictly to modify what is being saved to the database, but the name
doesn't really imply that.

The more pressing problems I've had, are with custom datastructures.
We use a few different field types, such as a ListField,
SerializedField, and a JSONField. Each of these uses a custom data
type. The issue with these, that, while it's easy to serialize the
data into the database (using get_db_pre_value), it's not as easy to
manage the value when it's taken from the database, or set directly.

I'd propose two new hooks, which work like to_python. One would be
called to set the data when it's pulled out of the database, and the
other would be called when it's set normally. Possibly even just
leaving to_python as this, and add the new database hook.

Here is a rough example, of the current implementation to make this
work:

(source: http://www.djangosnippets.org/snippets/377/)

class JSONField(models.TextField):
    def db_type(self):
        return 'text'

    def pre_save(self, model_instance, add):
        value = getattr(model_instance, self.attname, None)
        return dumps(value)

    def contribute_to_class(self, cls, name):
        super(JSONField, self).contribute_to_class(cls, name)
        dispatcher.connect(self.post_init, signal=signals.post_init,
sender=cls)

        def get_json(model_instance):
            return dumps(getattr(model_instance, self.attname, None))
        setattr(cls, 'get_%s_json' % self.name, get_json)

        def set_json(model_instance, json):
            return setattr(model_instance, self.attname, loads(json))
        setattr(cls, 'set_%s_json' % self.name, set_json)

    def post_init(self, instance=None):
        value = self.value_from_object(instance)
        if (value):
            setattr(instance, self.attname, loads(value))
        else:
            setattr(instance, self.attname, None)

This handles setting it from the database just fine, and you don't
need to worry about to_python (as we're keeping the original data
structure). The problem with this example, is it requires using a
signal, and IMHO is a lot more work that it could be.

Now if we were to add some kind of API which makes it easier to handle
the "post_init" as seen above:

class JSONField(models.TextField):
    __metaclass__ = models.SubfieldBase

    def get_value_from_db(self, value):
        if not value: return None
        return simplejson.loads(value)

    def get_db_prep_value(self, value):
        if value is None: return
        return simplejson.dumps(value)

Overall, the changes would be to simply provide more explicit hooks in
the API.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to 
django-developers+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to