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 django-users@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-users
-~----------~----~----~----~------~----~------~--~---

Reply via email to