开发者

to_python() never gets called (even in case of __metaclass__ = models.SubfieldBase)

开发者 https://www.devze.com 2023-02-16 21:14 出处:网络
Some time ago, as part of the process of learning Python+Django, I decided to write a custom MySQL-specific model field for the BIT column type. Unfortunately, I\'ve ran into a problem.

Some time ago, as part of the process of learning Python+Django, I decided to write a custom MySQL-specific model field for the BIT column type. Unfortunately, I've ran into a problem.

The project: contains a single "main" app

The "main" app: contains all of the standard files created by "python manage.py startapp", plus extfields.py

The contents of extfields.py are as follows:

from django.db import models
import re
import bitstring

class BitField(models.Field):
    description = 'A class representing a field of type "BIT" (MySQL-specific)'
    __metaclass__ = models.SubfieldBase

    def __init__(self, *args, **kwargs):
        bf_size = int(kwargs.pop('bitfield_size', 0))

        assert bf_size > 0, '%ss must have a positive bitfield_size' % self.__class__.__name__
        self._bf_size = bf_size

        super(BitField, self).__init__(*args, **kwargs)


    def db_type(self):
        return 'BIT(%d)' % self._bf_size


    def to_python(self, value):
        print('to_python starts')

        if isinstance(value, bitstring.BitArray):
            return value    

        value = str(value)

        regex = re.compile('^[01]{%d}$' % self._bf_size)

        assert regex.search(value) == True, 'The value must be a bit string.'

        print('to_python ends')

        return bitstring.BitArray(bin=value)


    def get_db_prep_value(self, value):
        return value.bin

The contents of models.py:

from django.db import models
import extfields

class MessageManager(models.Manager):
    """
    This manager is solely for the Message model. We need to alter the default
    QuerySet so that we'll get the correct values for the attributes bit field   
    """
    def get_query_set(self):
        return super(MessageManager, self).get_query_set().defer(
            'attributes'
        ).extra(
            select={'attributes': 'BIN(attributes)'}
        )


class Message(models.Model):
    attributes = extfields.BitField(bitfield_size=15)

    objects = MessageManager()

When I use the python shell (via python manage.py shell), I get the following:

>>> from main import models
>>> m = models.Message.objects.get(pk=1)
>>> m
<Message_Deferred_attributes: Message_Deferred_attributes object>
>>> m.attributes
u'1110001110'
>>> type(m.attributes)
<type 'unicode'>
>>> m.attributes = '1312312'
>>> m.attributes
'1312312'

As you see, the m.attributes is a plain string, instead of bitstring.BitArray instance.

Could someone please tell me where I've made a mistake?

I'm using Python 2.6.5 on Ubuntu 10.04. The bitstring module I import is this one: http://code.google.com/p/python-bitstring/. Python-django package version is 1.1.1-2ubuntu1.3.

EDIT (in response to emulbreh's comment):

right now my to_python() definition looks like this:

def to_p开发者_开发知识库ython(self, value):
        print 'to_python'

        if isinstance(value, bitstring.BitArray):
            return value    

        print type(value)    
        value = str(value)
        print'\n'
        print value
        print '\n'
        print type(value)        

        regex = re.compile('^[01]{%d}$' % self._bf_size)

        assert regex.search(value) == True, 'The value must be a bit string.'
        value = bitstring.BitArray(bin=value)
        print '\n'
        print type(value)
        print 'End of to_python'

        return value

The console output is:

to_python() never gets called (even in case of __metaclass__ = models.SubfieldBase)

After this, an AssertionError is raised.


You don't need to do anything special in the QuerySet to support a custom field. You currently defer() your field and then add a raw extra(select=) attribute that coincidentally has the same name as your field. If you just remove the custom manager (or the .defer().extra() calls) you should be fine.

0

精彩评论

暂无评论...
验证码 换一张
取 消