subclassing.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. """
  2. Convenience routines for creating non-trivial Field subclasses, as well as
  3. backwards compatibility utilities.
  4. Add SubfieldBase as the metaclass for your Field subclass, implement
  5. to_python() and the other necessary methods and everything will work
  6. seamlessly.
  7. """
  8. class SubfieldBase(type):
  9. """
  10. A metaclass for custom Field subclasses. This ensures the model's attribute
  11. has the descriptor protocol attached to it.
  12. """
  13. def __new__(cls, name, bases, attrs):
  14. new_class = super(SubfieldBase, cls).__new__(cls, name, bases, attrs)
  15. new_class.contribute_to_class = make_contrib(
  16. new_class, attrs.get('contribute_to_class')
  17. )
  18. return new_class
  19. class Creator(object):
  20. """
  21. A placeholder class that provides a way to set the attribute on the model.
  22. """
  23. def __init__(self, field):
  24. self.field = field
  25. def __get__(self, obj, type=None):
  26. if obj is None:
  27. return self
  28. return obj.__dict__[self.field.name]
  29. def __set__(self, obj, value):
  30. obj.__dict__[self.field.name] = self.field.to_python(value)
  31. def make_contrib(superclass, func=None):
  32. """
  33. Returns a suitable contribute_to_class() method for the Field subclass.
  34. If 'func' is passed in, it is the existing contribute_to_class() method on
  35. the subclass and it is called before anything else. It is assumed in this
  36. case that the existing contribute_to_class() calls all the necessary
  37. superclass methods.
  38. """
  39. def contribute_to_class(self, cls, name):
  40. if func:
  41. func(self, cls, name)
  42. else:
  43. super(superclass, self).contribute_to_class(cls, name)
  44. setattr(cls, self.name, Creator(self))
  45. return contribute_to_class