Let’s say you want to create a wrapper class around a Django model. For convenience, you want your wrapper class to forward any direct field access calls to the model it wraps. That is, you want the following code to work:
class MyModel(models.Model): slug = models.SlugField() class Wrapper(object): model = MyModel def __init__(self, w): self.w = w w = MyModel(slug='slug') w = Wrapper(w) print w.slug
This boils down to dynamically adding properties to the
Wrapper class that forward the call to and return the value from the wrapped instance.
Let’s also say that you want to create a base
Wrapper class which can be used for all wrapper classes around your models.
One way to do it would be to use the
__getattr__ function. Keep in mind we are only interested in responding to calls to fields, not every attribute of the model instance.
class Wrapper(object): def __init__(self, queryset): self.qs = queryset self._qs_field_names = [f.name for f in self.qs._meta.fields] def __getattr__(self, name): if name in self._qs_field_names: return getattr(self.qs, name) return super(Wrapper, self).__getattr__(name)
However, this means that all subclasses that want to override
__getattr__ will have to remember to call
super as well. This approach works well; on the other hand, it puts an extra burden to developers: they have to know the internals of the parent class whenever they need to override an internal function like
The other way is to use metaclasses and override the
def name_getter(obj, name): """Closure to creatre a function that operates on the given name.""" def getter(obj): return getattr(obj.qs, name) return getter class WrapperMeta(type): def __new__(meta, name, bases, dct): cls = super(ServiceMeta, meta).__new__(meta, name, bases, dct) if cls.model is not None: # Ignore base class case for f in cls.model._meta.fields: setattr(cls, f.name, property(name_getter(cls, f.name))) return cls class Wrapper(object): __metaclass__ = WrapperMeta model = None def __init__(self, queryset): self.qs = queryset
WrapperMeta metaclass overrides the
__new__ method, so that it will automatically add a
property to the derived classes for each field in the base class.
property function takes as (first) argument a function that takes one argument (the object, i.e.,
self) and returns a value. Since we create those properties dynamically for every attribute, we need to create a function that remembers the name of the attribute any specific property handles. Thus, we use a closure,
name_getter function stores the name
name of the attribute we want to access and returns a function that, given an object, will return the value of its attribute named
This allows us to create simple wrapper classes for a django model:
class MyWrapper(Wrapper): model = MyModel w = MyWrapper(MyModel(slug='slug')) print w.slug