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