I'm trying to implement a custom Django field that would keep in DB a name of one of my model classes (e. g. "Cat", "Dog", "Restaurant", "SuperPrivateCommercialDataPiece", etc.) and return the class object when requested:
class Cat(models.Model):
...
class SomeDataPiece(models.Model):
relatedTo = MyGloriousFieldType(null=True)
...
newPiece = SomeDataPiece()
newPiece.relatedTo = Cat
print newPiece.relatedTo # should print <class 'myproj.myapp.models.Cat'>
And I've really made this. I've subclassed models.Field
, set __metaclass__
, etc.:
class MyGloriousFieldType(models.Field):
description = "No description, just gloriosity."
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 40
super(BlockTypeField, self).__init__(*args, **kwargs)
def db_type(self, connection):
return 'char(40)'
def to_python(self, value):
if not value:
return None
elif inspect.isclass(value) and issubclass(value, models.Model):
return value
elif hasattr(myproj.myapp.models, value):
return getattr(myproj.myapp.models, value)
else:
raise ValidationError("Invalid model name string")
def get_db_prep_value(self, value, **kwargs):
if inspect.isclass(value) and issubclass(value, models.Model):
return value.__name__
else:
# it is actually never raised
raise ValidationError("Invalid value, Model subclass expected")
def value_to_string(self, instance):
value = self._get_val_from_obj(obj)
return self.get_prep_value(value)
And in the code above it works just as expected. I've also registered some of my models containing such fields in admin, and now I can even create such objects; text input is created for MyGloriousFieldType fields.
The crisis begins when I try to edit already existing object with MyGloriousFieldType field. First, it fills the text field with "Cat object" instead of just "Cat". Second, when I change it back to "Cat" and click "save changes" it gives an error:
TypeError at /admin/myapp/somedatapiece/3/
unbound method prepare_database_save() must be
called with Cat instance as first argument (got
MyGloriousFieldType instance instead)
So, what am I doing wrong?
Well, I've figured that out. The problem is inside the very Django core. Fields just can't return Python objects containing some
models.Field
attributes likeprepare_database_save
, otherwise an error occures. My hack to solve that was to return tuple(MyClassObject, )
instead of justMyClassObject
into_python()
. Odd, but what can I do... Maybe I'll try to post an issue somewhere on Django's Github page.