Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django custom field with multiple inheritance

I have two custom Django fields, a JSONField and a CompressedField, both of which work well. I would like to also have a CompressedJSONField, and I was rather hoping I could do this:

class CompressedJSONField(JSONField, CompressedField):
    pass

but on import I get:

RuntimeError: maximum recursion depth exceeded while calling a Python object

I can find information about using models with multiple inheritance in Django, but nothing about doing the same with fields. Should this be possible? Or should I just give up at this stage?

edit:

Just to be clear, I don't think this has anything to do with the specifics of my code, as the following code has exactly the same problem:

class CustomField(models.TextField, models.CharField):
    pass

edit 2:

I'm using Python 2.6.6 and Django 1.3 at present. Here is the full code of my stripped-right-down test example:

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

as soon as I add the compressed_json_field = CompressedJSONField() line I get errors when initializing Django.

like image 750
simon Avatar asked Nov 15 '22 01:11

simon


1 Answers

after doing a few quick tests i found that if you remove the metaclass from the JSON and compressed fields and put it in the compressedJSON field it compiles. if you then need the JSON or Compressed fields then subclass them and jusst add the __metaclass__ = models.SubfieldBase

i have to admit that i didn't do any heavy testing with this:

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

if you then want to uses the JSON and Commpressed fields separately i assume this idea will work:

class JSONFieldSubClass(JSONField):
    __metaclass__ = models.SubfieldBase

Honestly ... I don't really understand any of this.

EDIT base method hack

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

the other way is to make the to_python() on the classes have unique names and call them in your inherited classes to_python() methods

or maybe check out this answer

EDIT after some reading if you implement a call to super(class, self).method(args) in the first base to_python() then it will call the second base. If you use super consistently then you shouldn't have any problems. http://docs.python.org/library/functions.html#super is worth checking out and 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    
like image 97
James Khoury Avatar answered Dec 10 '22 20:12

James Khoury