web/lib/django/db/models/fields/subclassing.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 """
     1 """
     2 Convenience routines for creating non-trivial Field subclasses.
     2 Convenience routines for creating non-trivial Field subclasses, as well as
       
     3 backwards compatibility utilities.
     3 
     4 
     4 Add SubfieldBase as the __metaclass__ for your Field subclass, implement
     5 Add SubfieldBase as the __metaclass__ for your Field subclass, implement
     5 to_python() and the other necessary methods and everything will work seamlessly.
     6 to_python() and the other necessary methods and everything will work seamlessly.
     6 """
     7 """
     7 
     8 
     8 class SubfieldBase(type):
     9 from inspect import getargspec
       
    10 from warnings import warn
       
    11 
       
    12 def call_with_connection(func):
       
    13     arg_names, varargs, varkwargs, defaults = getargspec(func)
       
    14     updated = ('connection' in arg_names or varkwargs)
       
    15     if not updated:
       
    16         warn("A Field class whose %s method hasn't been updated to take a "
       
    17             "`connection` argument." % func.__name__,
       
    18             PendingDeprecationWarning, stacklevel=2)
       
    19 
       
    20     def inner(*args, **kwargs):
       
    21         if 'connection' not in kwargs:
       
    22             from django.db import connection
       
    23             kwargs['connection'] = connection
       
    24             warn("%s has been called without providing a connection argument. " %
       
    25                 func.__name__, PendingDeprecationWarning,
       
    26                 stacklevel=1)
       
    27         if updated:
       
    28             return func(*args, **kwargs)
       
    29         if 'connection' in kwargs:
       
    30             del kwargs['connection']
       
    31         return func(*args, **kwargs)
       
    32     return inner
       
    33 
       
    34 def call_with_connection_and_prepared(func):
       
    35     arg_names, varargs, varkwargs, defaults = getargspec(func)
       
    36     updated = (
       
    37         ('connection' in arg_names or varkwargs) and
       
    38         ('prepared' in arg_names or varkwargs)
       
    39     )
       
    40     if not updated:
       
    41         warn("A Field class whose %s method hasn't been updated to take "
       
    42             "`connection` and `prepared` arguments." % func.__name__,
       
    43             PendingDeprecationWarning, stacklevel=2)
       
    44 
       
    45     def inner(*args, **kwargs):
       
    46         if 'connection' not in kwargs:
       
    47             from django.db import connection
       
    48             kwargs['connection'] = connection
       
    49             warn("%s has been called without providing a connection argument. " %
       
    50                 func.__name__, PendingDeprecationWarning,
       
    51                 stacklevel=1)
       
    52         if updated:
       
    53             return func(*args, **kwargs)
       
    54         if 'connection' in kwargs:
       
    55             del kwargs['connection']
       
    56         if 'prepared' in kwargs:
       
    57             del kwargs['prepared']
       
    58         return func(*args, **kwargs)
       
    59     return inner
       
    60 
       
    61 class LegacyConnection(type):
       
    62     """
       
    63     A metaclass to normalize arguments give to the get_db_prep_* and db_type
       
    64     methods on fields.
       
    65     """
       
    66     def __new__(cls, names, bases, attrs):
       
    67         new_cls = super(LegacyConnection, cls).__new__(cls, names, bases, attrs)
       
    68         for attr in ('db_type', 'get_db_prep_save'):
       
    69             setattr(new_cls, attr, call_with_connection(getattr(new_cls, attr)))
       
    70         for attr in ('get_db_prep_lookup', 'get_db_prep_value'):
       
    71             setattr(new_cls, attr, call_with_connection_and_prepared(getattr(new_cls, attr)))
       
    72         return new_cls
       
    73 
       
    74 class SubfieldBase(LegacyConnection):
     9     """
    75     """
    10     A metaclass for custom Field subclasses. This ensures the model's attribute
    76     A metaclass for custom Field subclasses. This ensures the model's attribute
    11     has the descriptor protocol attached to it.
    77     has the descriptor protocol attached to it.
    12     """
    78     """
    13     def __new__(cls, base, name, attrs):
    79     def __new__(cls, base, name, attrs):
    24         self.field = field
    90         self.field = field
    25 
    91 
    26     def __get__(self, obj, type=None):
    92     def __get__(self, obj, type=None):
    27         if obj is None:
    93         if obj is None:
    28             raise AttributeError('Can only be accessed via an instance.')
    94             raise AttributeError('Can only be accessed via an instance.')
    29         return obj.__dict__[self.field.name]        
    95         return obj.__dict__[self.field.name]
    30 
    96 
    31     def __set__(self, obj, value):
    97     def __set__(self, obj, value):
    32         obj.__dict__[self.field.name] = self.field.to_python(value)
    98         obj.__dict__[self.field.name] = self.field.to_python(value)
    33 
    99 
    34 def make_contrib(func=None):
   100 def make_contrib(func=None):