You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
1.7 KiB
50 lines
1.7 KiB
""" |
|
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
|
|
|