Django自定义字段与多重继承

5 投票
2 回答
1691 浏览
提问于 2025-04-16 14:57

我有两个自定义的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 个回答

-2

要搞清楚你到底什么时候会遇到这个错误,其实挺难的。不过从Django的代码来看,有类似的实现方式(就是多重继承)

可以参考: class ImageFieldFile(ImageFile, FieldFile)
在django/db/models/fields里

3

经过一些快速测试,我发现如果把元类从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    

撰写回答