George Sakkis wrote:
> After digging a little into Django's guts, I came up with a simple
> field that is also a property:
>
> from django.db.models import Field
>
> class PropertyField(Field,property):
> def __init__(self, func, **kwds):
> Field.__init__(self,**kwds)
> # allow but ignore attribute setting
> property.__init__(self, fget=func, fset=lambda s,val:None)
>
> def contribute_to_class(self, cls, name):
> Field.contribute_to_class(self,cls,name)
> # add self in the class; properties don't work per instance
> setattr(cls, self.attname, self)
>
> # example
> from django.db.models import Model, IntegerField
>
> class Foo(Model):
> number = IntegerField()
> square = PropertyField(lambda self: self.number**2)
>
> >>> x = models.Foo(number=3)
> >>> print x.number, x.square
> 3 9
> >>> print dict((f.attname, getattr(x,f.attname)) for f in
> >>> models.Foo._meta.fields)
> {'square': 9, 'id': None, 'number': 3}
I just came up with a more flexible solution that uses a decorator
instead of inheritance. Rather than creating a new field, it modifies
an existing one so that the latter attaches a property to its model.
Here's the code:
def PropertyField(field):
'''Return a decorator that wraps a function as a property and
attaches it to the given field's model.
'''
def deco(func):
# let the property have a no-op setter so that it doesn't brake
Model.__init__
prop = property(fget=func, fset=lambda self,val:None)
# hijack the field's contribute_to_class method with one that
# adds the property to the class
old_contribute_to_class = field.contribute_to_class
def new_contribute_to_class(cls, name):
old_contribute_to_class(cls,name)
setattr(cls, field.attname, prop)
field.contribute_to_class = new_contribute_to_class
# XXX: Contrary to most decorators, this doesn't return
# a function but the (modified) field !
return field
return deco
And here's the previous example using the new PropertyField:
from django.db.models import Model, IntegerField
class Foo(Model):
number = IntegerField()
@PropertyField(IntegerField(blank=True))
def square(self):
return self.number**2
>>> x = models.Foo(number=3)
>>> print x.number, x.square
3 9
>>> print dict((f.attname, getattr(x,f.attname)) for f in
>>> models.Foo._meta.fields)
> {'square': 9, 'id': None, 'number': 3}
As before, this requires further modifications to the framework so that
Django doesn't attempt so retrieve or store property fields from/to the
database.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django users" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-users
-~----------~----~----~----~------~----~------~--~---