Ticket #10728: SubfieldBase-#10728-r12890.patch

File SubfieldBase-#10728-r12890.patch, 4.1 KB (added by george.sakkis@…, 14 years ago)
  • django/db/models/fields/subclassing.py

     
    7676    A metaclass for custom Field subclasses. This ensures the model's attribute
    7777    has the descriptor protocol attached to it.
    7878    """
    79     def __new__(cls, base, name, attrs):
    80         new_class = super(SubfieldBase, cls).__new__(cls, base, name, attrs)
    81         new_class.contribute_to_class = make_contrib(
    82                 attrs.get('contribute_to_class'))
    83         return new_class
     79    def __init__(cls, name, base, attrs):
     80        def contribute_to_class(self, model_cls, name):
     81            assert isinstance(self, cls)
     82            super(cls, self).contribute_to_class(model_cls, name)
     83            setattr(model_cls, name, Creator(self))
     84        cls.contribute_to_class = contribute_to_class
    8485
    8586class Creator(object):
    8687    """
     
    9697
    9798    def __set__(self, obj, value):
    9899        obj.__dict__[self.field.name] = self.field.to_python(value)
    99 
    100 def make_contrib(func=None):
    101     """
    102     Returns a suitable contribute_to_class() method for the Field subclass.
    103 
    104     If 'func' is passed in, it is the existing contribute_to_class() method on
    105     the subclass and it is called before anything else. It is assumed in this
    106     case that the existing contribute_to_class() calls all the necessary
    107     superclass methods.
    108     """
    109     def contribute_to_class(self, cls, name):
    110         if func:
    111             func(self, cls, name)
    112         else:
    113             super(self.__class__, self).contribute_to_class(cls, name)
    114         setattr(cls, self.name, Creator(self))
    115 
    116     return contribute_to_class
  • tests/modeltests/field_subclassing/models.py

     
    66from django.db import models
    77from django.utils.encoding import force_unicode
    88
    9 from fields import Small, SmallField, JSONField
     9from fields import Small, SmallField, SmallerField, JSONField
    1010
    1111
    1212class MyModel(models.Model):
     
    1919class DataModel(models.Model):
    2020    data = JSONField()
    2121
     22def make_smallermodel():
     23    """
     24    Isolate SmallerModel class definition.
     25
     26    When the test fails, it fails at class definition with a long
     27    stack trace. This confuses test discovery, so wrap it in a function.
     28    """
     29    try:
     30        class SmallerModel(models.Model):
     31            other_data = SmallerField(default='example')
     32    except RuntimeError: # Maximum recursion depth
     33        raise RuntimeError("Couldn't subclass field")
     34    else:
     35        return SmallerModel
     36
    2237__test__ = {'API_TESTS': ur"""
    2338# Creating a model with custom fields is done as per normal.
    2439>>> s = Small(1, 2)
     
    6176>>> obj.object == m
    6277True
    6378
     79# Test custom field subclassing.
     80>>> SmallerModel = make_smallermodel()
     81>>> sm = SmallerModel()
     82>>> sm.other_data = 'plop'
     83>>> str(sm.other_data)
     84"pl"
     85
    6486# Test retrieving custom field data
    6587>>> m.delete()
    6688>>> m1 = MyModel(name="1", data=Small(1, 2))
  • tests/modeltests/field_subclassing/fields.py

     
    5151        raise TypeError('Invalid lookup type: %r' % lookup_type)
    5252
    5353
     54class SmallerField(SmallField):
     55    """
     56    Checks that the SubfieldBase metaclass works with inheritance.
     57    """
     58
    5459class JSONField(models.TextField):
    5560    __metaclass__ = models.SubfieldBase
    56    
     61
    5762    description = ("JSONField automatically serializes and desializes values to "
    5863        "and from JSON.")
    59    
     64
    6065    def to_python(self, value):
    6166        if not value:
    6267            return None
    63        
    6468        if isinstance(value, basestring):
    6569            value = json.loads(value)
    6670        return value
    67    
     71
    6872    def get_db_prep_save(self, value):
    6973        if value is None:
    7074            return None
Back to Top