app/django/db/models/fields/subclassing.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 """
       
     2 Convenience routines for creating non-trivial Field subclasses.
       
     3 
       
     4 Add SubfieldBase as the __metaclass__ for your Field subclass, implement
       
     5 to_python() and the other necessary methods and everything will work seamlessly.
       
     6 """
       
     7 
       
     8 from django.utils.maxlength import LegacyMaxlength
       
     9 
       
    10 class SubfieldBase(LegacyMaxlength):
       
    11     """
       
    12     A metaclass for custom Field subclasses. This ensures the model's attribute
       
    13     has the descriptor protocol attached to it.
       
    14     """
       
    15     def __new__(cls, base, name, attrs):
       
    16         new_class = super(SubfieldBase, cls).__new__(cls, base, name, attrs)
       
    17         new_class.contribute_to_class = make_contrib(
       
    18                 attrs.get('contribute_to_class'))
       
    19         return new_class
       
    20 
       
    21 class Creator(object):
       
    22     """
       
    23     A placeholder class that provides a way to set the attribute on the model.
       
    24     """
       
    25     def __init__(self, field):
       
    26         self.field = field
       
    27 
       
    28     def __get__(self, obj, type=None):
       
    29         if obj is None:
       
    30             raise AttributeError('Can only be accessed via an instance.')
       
    31         return obj.__dict__[self.field.name]        
       
    32 
       
    33     def __set__(self, obj, value):
       
    34         obj.__dict__[self.field.name] = self.field.to_python(value)
       
    35 
       
    36 def make_contrib(func=None):
       
    37     """
       
    38     Returns a suitable contribute_to_class() method for the Field subclass.
       
    39 
       
    40     If 'func' is passed in, it is the existing contribute_to_class() method on
       
    41     the subclass and it is called before anything else. It is assumed in this
       
    42     case that the existing contribute_to_class() calls all the necessary
       
    43     superclass methods.
       
    44     """
       
    45     def contribute_to_class(self, cls, name):
       
    46         if func:
       
    47             func(self, cls, name)
       
    48         else:
       
    49             super(self.__class__, self).contribute_to_class(cls, name)
       
    50         setattr(cls, self.name, Creator(self))
       
    51 
       
    52     return contribute_to_class
       
    53