Django自定义字段与多重继承
我有两个自定义的Django字段,一个是 JSONField
,另一个是 CompressedField
,这两个都能正常工作。我想再创建一个 CompressedJSONField
,我本来希望可以这样做:
class CompressedJSONField(JSONField, CompressedField):
pass
但是在导入时我遇到了:
RuntimeError: maximum recursion depth exceeded while calling a Python object
我能找到关于在Django中使用多重继承的模型的信息,但没有找到关于字段的相关内容。这应该是可行的吗?还是我现在就该放弃了?
补充:
为了更清楚,我认为这和我的代码具体内容没有关系,因为以下代码也有完全相同的问题:
class CustomField(models.TextField, models.CharField):
pass
补充 2:
我现在使用的是Python 2.6.6和Django 1.3。这是我简化后的测试示例的完整代码:
customfields.py
from django.db import models
class CompressedField(models.TextField):
""" Standard TextField with automatic compression/decompression. """
__metaclass__ = models.SubfieldBase
description = 'Field which compresses stored data.'
def to_python(self, value):
return value
def get_db_prep_value(self, value, **kwargs):
return super(CompressedField, self)\
.get_db_prep_value(value, prepared=True)
class JSONField(models.TextField):
""" JSONField with automatic serialization/deserialization. """
__metaclass__ = models.SubfieldBase
description = 'Field which stores a JSON object'
def to_python(self, value):
return value
def get_db_prep_save(self, value, **kwargs):
return super(JSONField, self).get_db_prep_save(value, **kwargs)
class CompressedJSONField(JSONField, CompressedField):
pass
models.py
from django.db import models
from customfields import CompressedField, JSONField, CompressedJSONField
class TestModel(models.Model):
name = models.CharField(max_length=150)
compressed_field = CompressedField()
json_field = JSONField()
compressed_json_field = CompressedJSONField()
def __unicode__(self):
return self.name
一旦我添加了 compressed_json_field = CompressedJSONField()
这一行,初始化Django时就会出现错误。
2 个回答
要搞清楚你到底什么时候会遇到这个错误,其实挺难的。不过从Django的代码来看,有类似的实现方式(就是多重继承)
可以参考: class ImageFieldFile(ImageFile, FieldFile)
在django/db/models/fields里
经过一些快速测试,我发现如果把元类从JSON和压缩字段中去掉,放到compressedJSON字段里,就可以编译通过。如果之后需要使用JSON或压缩字段,可以通过子类化它们,然后只需添加__metaclass__ = models.SubfieldBase
。
我得承认,我没有进行过深入的测试:
from django.db import models
class CompressedField(models.TextField):
""" Standard TextField with automatic compression/decompression. """
description = 'Field which compresses stored data.'
def to_python(self, value):
return value
def get_db_prep_value(self, value, **kwargs):
return super(CompressedField, self).get_db_prep_value(value, prepared=True)
class JSONField(models.TextField):
""" JSONField with automatic serialization/deserialization. """
description = 'Field which stores a JSON object'
def to_python(self, value):
return value
def get_db_prep_save(self, value, **kwargs):
return super(JSONField, self).get_db_prep_save(value, **kwargs)
class CompressedJSONField(JSONField, CompressedField):
__metaclass__ = models.SubfieldBase
class TestModel(models.Model):
name = models.CharField(max_length=150)
#compressed_field = CompressedField()
#json_field = JSONField()
compressed_json_field = CompressedJSONField()
def __unicode__(self):
return self.name
如果你想单独使用JSON和压缩字段,我想这个方法应该是可行的:
class JSONFieldSubClass(JSONField):
__metaclass__ = models.SubfieldBase
老实说……我对这些内容并不是很理解。
编辑:基础方法的技巧
class CompressedJSONField(JSONField, CompressedField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
value = JSONField.to_python(self, value)
value = CompressedField.to_python(self, value)
return value
另一种方法是让类中的to_python()方法有独特的名字,并在你继承的类的to_python()方法中调用它们。
或者你可以看看这个回答。
编辑:经过一些阅读,如果你在第一个基础的to_python()中实现调用super(class, self).method(args)
,那么它会调用第二个基础。如果你始终如一地使用super,就不应该有任何问题。可以看看这个链接http://docs.python.org/library/functions.html#super和这个http://www.artima.com/weblogs/viewpost.jsp?thread=237121。
class base1(object):
def name(self, value):
print "base1", value
super(base1, self).name(value)
def to_python(self, value):
value = value + " base 1 "
if(hasattr(super(base1, self), "to_python")):
value = super(base1, self).to_python(value)
return value
class base2(object):
def name(self, value):
print "base2", value
def to_python(self, value):
value = value + " base 2 "
if(hasattr(super(base2, self), "to_python")):
value = super(base2, self).to_python(value)
return value
class superClass(base1, base2):
def name(self, value):
super(superClass, self).name(value)
print "super Class", value