/lib/django-1.2/django/db/models/fields/subclassing.py
https://github.com/theosp/google_appengine · Python · 117 lines · 94 code · 7 blank · 16 comment · 14 complexity · 701828d7c06e3e8c456a1d19234e4b47 MD5 · raw file
- """
- Convenience routines for creating non-trivial Field subclasses, as well as
- backwards compatibility utilities.
- Add SubfieldBase as the __metaclass__ for your Field subclass, implement
- to_python() and the other necessary methods and everything will work seamlessly.
- """
- from inspect import getargspec
- from warnings import warn
- def call_with_connection(func):
- arg_names, varargs, varkwargs, defaults = getargspec(func)
- updated = ('connection' in arg_names or varkwargs)
- if not updated:
- warn("A Field class whose %s method hasn't been updated to take a "
- "`connection` argument." % func.__name__,
- PendingDeprecationWarning, stacklevel=2)
- def inner(*args, **kwargs):
- if 'connection' not in kwargs:
- from django.db import connection
- kwargs['connection'] = connection
- warn("%s has been called without providing a connection argument. " %
- func.__name__, PendingDeprecationWarning,
- stacklevel=1)
- if updated:
- return func(*args, **kwargs)
- if 'connection' in kwargs:
- del kwargs['connection']
- return func(*args, **kwargs)
- return inner
- def call_with_connection_and_prepared(func):
- arg_names, varargs, varkwargs, defaults = getargspec(func)
- updated = (
- ('connection' in arg_names or varkwargs) and
- ('prepared' in arg_names or varkwargs)
- )
- if not updated:
- warn("A Field class whose %s method hasn't been updated to take "
- "`connection` and `prepared` arguments." % func.__name__,
- PendingDeprecationWarning, stacklevel=2)
- def inner(*args, **kwargs):
- if 'connection' not in kwargs:
- from django.db import connection
- kwargs['connection'] = connection
- warn("%s has been called without providing a connection argument. " %
- func.__name__, PendingDeprecationWarning,
- stacklevel=1)
- if updated:
- return func(*args, **kwargs)
- if 'connection' in kwargs:
- del kwargs['connection']
- if 'prepared' in kwargs:
- del kwargs['prepared']
- return func(*args, **kwargs)
- return inner
- class LegacyConnection(type):
- """
- A metaclass to normalize arguments give to the get_db_prep_* and db_type
- methods on fields.
- """
- def __new__(cls, name, bases, attrs):
- new_cls = super(LegacyConnection, cls).__new__(cls, name, bases, attrs)
- for attr in ('db_type', 'get_db_prep_save'):
- setattr(new_cls, attr, call_with_connection(getattr(new_cls, attr)))
- for attr in ('get_db_prep_lookup', 'get_db_prep_value'):
- setattr(new_cls, attr, call_with_connection_and_prepared(getattr(new_cls, attr)))
- return new_cls
- class SubfieldBase(LegacyConnection):
- """
- A metaclass for custom Field subclasses. This ensures the model's attribute
- has the descriptor protocol attached to it.
- """
- def __new__(cls, name, bases, attrs):
- new_class = super(SubfieldBase, cls).__new__(cls, name, bases, attrs)
- new_class.contribute_to_class = make_contrib(
- new_class, 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(superclass, 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(superclass, self).contribute_to_class(cls, name)
- setattr(cls, self.name, Creator(self))
- return contribute_to_class