app/django/db/models/fields/subclassing.py
author Todd Larsen <tlarsen@google.com>
Wed, 21 Jan 2009 00:27:39 +0000
changeset 858 e79e7a22326f
parent 323 ff1a9aa48cfd
permissions -rw-r--r--
Add an export() view, and implement it as text/text for Document. For every Model except Document, the public() view is displayed for any attempts to access the export() view. Currently, the permissions for export() are the same as for public(). This seems reasonable for Document, since anyone could extract the raw HTML from the page source anyway. The permissions should probably be different for other types of exports, such as vCard or iCard exports of profiles, CSV exports of lists, etc. Patch by: Todd Larsen Review by: to-be-reviewed

"""
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