#7974 closed (wontfix)
cannot replace custom fields without using database format
Reported by: | mspang | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.0-alpha |
Severity: | Keywords: | ||
Cc: | Jacob | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
If you use to_python() to build a Python class for a custom field (as explained in the tutorial), you lose the ability to directly assign to that field. Example:
Model:
class Int(object): def __init__(self, value): self.int_value = value class IntField(models.Field): __metaclass__ = models.SubfieldBase def get_default(self): return 0 def to_python(self, value): return Int(int(value)) def get_db_prep_save(self, value): return value.int_value def db_type(): return 'int' class IntModel(models.Model): int_field = IntField()
Code:
>>> a = models.IntModel() >>> a.int_field <testproj.testapp.models.Int object at 0xf696cf8c> >>> a.int_field.int_value 0 >>> a.int_field.int_value = 10 >>> a.int_field.int_value 10 >>> a.int_field = models.Int(30) Traceback (most recent call last): File "<console>", line 1, in ? File "/opt/django/db/models/fields/subclassing.py", line 34, in __set__ obj.__dict__[self.field.name] = self.field.to_python(value) File "/home/mspang/testproj/testapp/models.py", line 97, in to_python return Int(int(value)) TypeError: int() argument must be a string or a number
We can never assign to int_field directly using its python type. This is especially crippling if the object itself is immutable, since in that case you have to serialize and deserialize the field once per change. To solve this, to_python() must not be called during assignment, only during database loads. Introducing a new method ('get_db_prep_load') for this purpose seems appropriate.
Change History (2)
comment:1 by , 16 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
comment:2 by , 16 years ago
Cc: | added |
---|
That works for my contrived example, but consider:
How would you write a field that stores data in the database in base64, and expects to be assigned non-base64 strings? You can't do it, because to_python() doesn't know whether it's coming from database or the user.
Overloading to_python() to do both "load from the database" and "set by user" is fairly limiting. Unfortunately I didn't have time to work up a patch that would cleanly separate them so Django 1.0 will also have this problem. You can't work around it by writing your own descriptor, either.
This is by design: SubfieldBase is a shortcut for using some transparent user-defined type. If you want to directly assign that type, you need to either write a laxer to_python[*], or else implement the descriptor on the field yourself.
[*] like this: