| """ |
| Convenience routines for creating non-trivial Field subclasses. |
| |
| Add SubfieldBase as the __metaclass__ for your Field subclass, implement |
| to_python() and the other necessary methods and everything will work seamlessly. |
| """ |
| |
| class SubfieldBase(type): |
| """ |
| A metaclass for custom Field subclasses. This ensures the model's attribute |
| has the descriptor protocol attached to it. |
| """ |
| def __new__(cls, base, name, attrs): |
| new_class = super(SubfieldBase, cls).__new__(cls, base, name, attrs) |
| new_class.contribute_to_class = make_contrib( |
| attrs.get('contribute_to_class')) |
| return new_class |
| |
| class Creator(object): |
| """ |
| A placeholder class that provides a way to set the attribute on the model. |
| """ |
| def __init__(self, field): |
| self.field = field |
| |
| def __get__(self, obj, type=None): |
| if obj is None: |
| raise AttributeError('Can only be accessed via an instance.') |
| return obj.__dict__[self.field.name] |
| |
| def __set__(self, obj, value): |
| obj.__dict__[self.field.name] = self.field.to_python(value) |
| |
| def make_contrib(func=None): |
| """ |
| Returns a suitable contribute_to_class() method for the Field subclass. |
| |
| If 'func' is passed in, it is the existing contribute_to_class() method on |
| the subclass and it is called before anything else. It is assumed in this |
| case that the existing contribute_to_class() calls all the necessary |
| superclass methods. |
| """ |
| def contribute_to_class(self, cls, name): |
| if func: |
| func(self, cls, name) |
| else: |
| super(self.__class__, self).contribute_to_class(cls, name) |
| setattr(cls, self.name, Creator(self)) |
| |
| return contribute_to_class |