2005-08-02 05:29:52 +08:00
from django . conf import settings
2005-07-13 09:25:57 +08:00
from django . core import formfields , validators
from django . core import db
from django . core . exceptions import ObjectDoesNotExist
2005-08-02 05:29:52 +08:00
from django . core . meta . fields import *
from django . utils . functional import curry
from django . utils . text import capfirst
2005-07-13 09:25:57 +08:00
import copy , datetime , os , re , sys , types
# Admin stages.
ADD , CHANGE , BOTH = 1 , 2 , 3
# Size of each "chunk" for get_iterator calls.
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
2005-08-02 05:29:52 +08:00
# Prefix (in Python path style) to location of models.
2005-07-13 09:25:57 +08:00
MODEL_PREFIX = ' django.models '
# Methods on models with the following prefix will be removed and
# converted to module-level functions.
MODEL_FUNCTIONS_PREFIX = ' _module_ '
# Methods on models with the following prefix will be removed and
# converted to manipulator methods.
MANIPULATOR_FUNCTIONS_PREFIX = ' _manipulator_ '
LOOKUP_SEPARATOR = ' __ '
####################
# HELPER FUNCTIONS #
####################
2005-07-22 21:02:27 +08:00
# Django currently supports two forms of ordering.
# Form 1 (deprecated) example:
# order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM'))
# Form 2 (new-style) example:
# order_by=('-pub_date', 'headline', '?')
# Form 1 is deprecated and will no longer be supported for Django's first
# official release. The following code converts from Form 1 to Form 2.
LEGACY_ORDERING_MAPPING = { ' ASC ' : ' _ ' , ' DESC ' : ' -_ ' , ' RANDOM ' : ' ? ' }
def handle_legacy_orderlist ( order_list ) :
if not order_list or isinstance ( order_list [ 0 ] , basestring ) :
return order_list
else :
2005-07-22 21:03:50 +08:00
import warnings
2005-07-22 21:02:27 +08:00
new_order_list = [ LEGACY_ORDERING_MAPPING [ j . upper ( ) ] . replace ( ' _ ' , str ( i ) ) for i , j in order_list ]
2005-07-22 21:03:50 +08:00
warnings . warn ( " %r ordering syntax is deprecated. Use %r instead. " % ( order_list , new_order_list ) , DeprecationWarning )
2005-07-22 21:02:27 +08:00
return new_order_list
2005-08-26 06:51:30 +08:00
def orderfield2column ( f , opts ) :
try :
return opts . get_field ( f , False ) . column
except FieldDoesNotExist :
return f
def orderlist2sql ( order_list , opts , prefix = ' ' ) :
2005-07-22 21:02:27 +08:00
output = [ ]
for f in handle_legacy_orderlist ( order_list ) :
if f . startswith ( ' - ' ) :
2005-08-26 06:51:30 +08:00
output . append ( ' %s %s DESC ' % ( prefix , orderfield2column ( f [ 1 : ] , opts ) ) )
2005-07-22 21:02:27 +08:00
elif f == ' ? ' :
2005-09-03 04:46:00 +08:00
output . append ( db . get_random_function_sql ( ) )
2005-07-22 21:02:27 +08:00
else :
2005-08-26 06:51:30 +08:00
output . append ( ' %s %s ASC ' % ( prefix , orderfield2column ( f , opts ) ) )
2005-07-22 21:02:27 +08:00
return ' , ' . join ( output )
2005-07-13 09:25:57 +08:00
def get_module ( app_label , module_name ) :
return __import__ ( ' %s . %s . %s ' % ( MODEL_PREFIX , app_label , module_name ) , ' ' , ' ' , [ ' ' ] )
def get_app ( app_label ) :
return __import__ ( ' %s . %s ' % ( MODEL_PREFIX , app_label ) , ' ' , ' ' , [ ' ' ] )
_installed_models_cache = None
def get_installed_models ( ) :
"""
Returns a list of installed " models " packages , such as foo . models ,
ellington . news . models , etc . This does NOT include django . models .
"""
global _installed_models_cache
if _installed_models_cache is not None :
return _installed_models_cache
_installed_models_cache = [ ]
for a in settings . INSTALLED_APPS :
try :
_installed_models_cache . append ( __import__ ( a + ' .models ' , ' ' , ' ' , [ ' ' ] ) )
except ImportError :
pass
return _installed_models_cache
_installed_modules_cache = None
def get_installed_model_modules ( core_models = None ) :
"""
Returns a list of installed models , such as django . models . core ,
ellington . news . models . news , foo . models . bar , etc .
"""
global _installed_modules_cache
if _installed_modules_cache is not None :
return _installed_modules_cache
_installed_modules_cache = [ ]
# django.models is a special case.
for submodule in ( core_models or [ ] ) :
_installed_modules_cache . append ( __import__ ( ' django.models. %s ' % submodule , ' ' , ' ' , [ ' ' ] ) )
for m in get_installed_models ( ) :
for submodule in getattr ( m , ' __all__ ' , [ ] ) :
2005-08-04 11:32:56 +08:00
mod = __import__ ( ' django.models. %s ' % submodule , ' ' , ' ' , [ ' ' ] )
try :
mod . _MODELS
except AttributeError :
pass # Skip model modules that don't actually have models in them.
else :
_installed_modules_cache . append ( mod )
2005-07-13 09:25:57 +08:00
return _installed_modules_cache
class LazyDate :
"""
Use in limit_choices_to to compare the field to dates calculated at run time
instead of when the model is loaded . For example : :
. . . limit_choices_to = { ' date__gt ' : meta . LazyDate ( days = - 3 ) } . . .
which will limit the choices to dates greater than three days ago .
"""
def __init__ ( self , * * kwargs ) :
self . delta = datetime . timedelta ( * * kwargs )
def __str__ ( self ) :
return str ( self . __get_value__ ( ) )
def __repr__ ( self ) :
return " <LazyDate: %s > " % self . delta
def __get_value__ ( self ) :
return datetime . datetime . now ( ) + self . delta
################
# MAIN CLASSES #
################
class FieldDoesNotExist ( Exception ) :
pass
class BadKeywordArguments ( Exception ) :
pass
class Options :
def __init__ ( self , module_name = ' ' , verbose_name = ' ' , verbose_name_plural = ' ' , db_table = ' ' ,
fields = None , ordering = None , unique_together = None , admin = None , has_related_links = False ,
where_constraints = None , object_name = None , app_label = None ,
exceptions = None , permissions = None , get_latest_by = None ,
order_with_respect_to = None , module_constants = None ) :
# Save the original function args, for use by copy(). Note that we're
# NOT using copy.deepcopy(), because that would create a new copy of
# everything in memory, and it's better to conserve memory. Of course,
# this comes with the important gotcha that changing any attribute of
# this object will change its value in self._orig_init_args, so we
# need to be careful not to do that. In practice, we can pull this off
# because Options are generally read-only objects, and __init__() is
# the only place where its attributes are manipulated.
# locals() is used purely for convenience, so we don't have to do
# something verbose like this:
# self._orig_init_args = {
# 'module_name': module_name,
# 'verbose_name': verbose_name,
# ...
# }
self . _orig_init_args = locals ( )
del self . _orig_init_args [ ' self ' ] # because we don't care about it.
# Move many-to-many related fields from self.fields into self.many_to_many.
self . fields , self . many_to_many = [ ] , [ ]
for field in ( fields or [ ] ) :
if field . rel and isinstance ( field . rel , ManyToMany ) :
self . many_to_many . append ( field )
else :
self . fields . append ( field )
self . module_name , self . verbose_name = module_name , verbose_name
self . verbose_name_plural = verbose_name_plural or verbose_name + ' s '
self . db_table , self . has_related_links = db_table , has_related_links
self . ordering = ordering or [ ]
self . unique_together = unique_together or [ ]
self . where_constraints = where_constraints or [ ]
self . exceptions = exceptions or [ ]
self . permissions = permissions or [ ]
self . object_name , self . app_label = object_name , app_label
self . get_latest_by = get_latest_by
if order_with_respect_to :
self . order_with_respect_to = self . get_field ( order_with_respect_to )
2005-07-22 21:02:27 +08:00
self . ordering = ( ' _order ' , )
2005-07-13 09:25:57 +08:00
else :
self . order_with_respect_to = None
self . module_constants = module_constants or { }
2005-07-20 13:46:00 +08:00
self . admin = admin
2005-07-13 09:25:57 +08:00
# Calculate one_to_one_field.
self . one_to_one_field = None
for f in self . fields :
if isinstance ( f . rel , OneToOne ) :
self . one_to_one_field = f
break
# Cache the primary-key field.
self . pk = None
for f in self . fields :
if f . primary_key :
self . pk = f
break
# If a primary_key field hasn't been specified, add an
# auto-incrementing primary-key ID field automatically.
if self . pk is None :
2005-08-26 06:51:30 +08:00
self . fields . insert ( 0 , AutoField ( name = ' id ' , verbose_name = ' ID ' , primary_key = True ) )
2005-07-13 09:25:57 +08:00
self . pk = self . fields [ 0 ]
2005-08-10 13:08:27 +08:00
# Cache whether this has an AutoField.
self . has_auto_field = False
for f in self . fields :
is_auto = isinstance ( f , AutoField )
if is_auto and self . has_auto_field :
raise AssertionError , " A model can ' t have more than one AutoField. "
elif is_auto :
self . has_auto_field = True
2005-07-13 09:25:57 +08:00
def __repr__ ( self ) :
return ' <Options for %s > ' % self . module_name
def copy ( self , * * kwargs ) :
args = self . _orig_init_args . copy ( )
args . update ( kwargs )
return self . __class__ ( * * args )
def get_model_module ( self ) :
return get_module ( self . app_label , self . module_name )
def get_content_type_id ( self ) :
" Returns the content-type ID for this object type. "
if not hasattr ( self , ' _content_type_id ' ) :
mod = get_module ( ' core ' , ' contenttypes ' )
self . _content_type_id = mod . get_object ( python_module_name__exact = self . module_name , package__label__exact = self . app_label ) . id
return self . _content_type_id
def get_field ( self , name , many_to_many = True ) :
"""
Returns the requested field by name . Raises FieldDoesNotExist on error .
"""
to_search = many_to_many and ( self . fields + self . many_to_many ) or self . fields
for f in to_search :
if f . name == name :
return f
raise FieldDoesNotExist , " name= %s " % name
def get_order_sql ( self , table_prefix = ' ' ) :
" Returns the full ' ORDER BY ' clause for this object, according to self.ordering. "
if not self . ordering : return ' '
pre = table_prefix and ( table_prefix + ' . ' ) or ' '
2005-08-26 06:51:30 +08:00
return ' ORDER BY ' + orderlist2sql ( self . ordering , self , pre )
2005-07-13 09:25:57 +08:00
def get_add_permission ( self ) :
return ' add_ %s ' % self . object_name . lower ( )
def get_change_permission ( self ) :
return ' change_ %s ' % self . object_name . lower ( )
def get_delete_permission ( self ) :
return ' delete_ %s ' % self . object_name . lower ( )
def get_rel_object_method_name ( self , rel_opts , rel_field ) :
# This method encapsulates the logic that decides what name to give a
# method that retrieves related many-to-one objects. Usually it just
# uses the lower-cased object_name, but if the related object is in
# another app, its app_label is appended.
#
# Examples:
#
# # Normal case -- a related object in the same app.
# # This method returns "choice".
# Poll.get_choice_list()
#
# # A related object in a different app.
# # This method returns "lcom_bestofaward".
# Place.get_lcom_bestofaward_list() # "lcom_bestofaward"
rel_obj_name = rel_field . rel . related_name or rel_opts . object_name . lower ( )
if self . app_label != rel_opts . app_label :
rel_obj_name = ' %s _ %s ' % ( rel_opts . app_label , rel_obj_name )
return rel_obj_name
def get_all_related_objects ( self ) :
try : # Try the cache first.
return self . _all_related_objects
except AttributeError :
module_list = get_installed_model_modules ( )
rel_objs = [ ]
for mod in module_list :
for klass in mod . _MODELS :
for f in klass . _meta . fields :
if f . rel and self == f . rel . to :
rel_objs . append ( ( klass . _meta , f ) )
if self . has_related_links :
# Manually add RelatedLink objects, which are a special case.
2005-09-03 00:34:14 +08:00
relatedlinks = get_module ( ' relatedlinks ' , ' relatedlinks ' )
2005-07-13 09:25:57 +08:00
# Note that the copy() is very important -- otherwise any
# subsequently loaded object with related links will override this
# relationship we're adding.
2005-09-03 00:34:14 +08:00
link_field = copy . copy ( relatedlinks . RelatedLink . _meta . get_field ( ' object_id ' ) )
2005-08-26 06:51:30 +08:00
link_field . rel = ManyToOne ( self . get_model_module ( ) . Klass , ' id ' ,
2005-08-10 05:08:00 +08:00
num_in_admin = 3 , min_num_in_admin = 3 , edit_inline = TABULAR ,
2005-07-13 09:25:57 +08:00
lookup_overrides = {
' content_type__package__label__exact ' : self . app_label ,
2005-09-03 00:34:14 +08:00
' content_type__python_module_name__exact ' : self . module_name ,
2005-07-13 09:25:57 +08:00
} )
2005-09-03 00:34:14 +08:00
rel_objs . append ( ( relatedlinks . RelatedLink . _meta , link_field ) )
2005-07-13 09:25:57 +08:00
self . _all_related_objects = rel_objs
return rel_objs
def get_inline_related_objects ( self ) :
return [ ( a , b ) for a , b in self . get_all_related_objects ( ) if b . rel . edit_inline ]
def get_all_related_many_to_many_objects ( self ) :
module_list = get_installed_model_modules ( )
rel_objs = [ ]
for mod in module_list :
for klass in mod . _MODELS :
try :
for f in klass . _meta . many_to_many :
if f . rel and self == f . rel . to :
rel_objs . append ( ( klass . _meta , f ) )
raise StopIteration
except StopIteration :
continue
return rel_objs
def get_ordered_objects ( self ) :
" Returns a list of Options objects that are ordered with respect to this object. "
if not hasattr ( self , ' _ordered_objects ' ) :
objects = [ ]
for klass in get_app ( self . app_label ) . _MODELS :
opts = klass . _meta
if opts . order_with_respect_to and opts . order_with_respect_to . rel \
and self == opts . order_with_respect_to . rel . to :
objects . append ( opts )
self . _ordered_objects = objects
return self . _ordered_objects
def has_field_type ( self , field_type ) :
"""
Returns True if this object ' s admin form has at least one of the given
field_type ( e . g . FileField ) .
"""
if not hasattr ( self , ' _field_types ' ) :
self . _field_types = { }
if not self . _field_types . has_key ( field_type ) :
try :
# First check self.fields.
for f in self . fields :
if isinstance ( f , field_type ) :
raise StopIteration
# Failing that, check related fields.
for rel_obj , rel_field in self . get_inline_related_objects ( ) :
for f in rel_obj . fields :
if isinstance ( f , field_type ) :
raise StopIteration
except StopIteration :
self . _field_types [ field_type ] = True
else :
self . _field_types [ field_type ] = False
return self . _field_types [ field_type ]
def _reassign_globals ( function_dict , extra_globals , namespace ) :
new_functions = { }
for k , v in function_dict . items ( ) :
# Get the code object.
code = v . func_code
# Recreate the function, but give it access to extra_globals and the
# given namespace's globals, too.
new_globals = { ' __builtins__ ' : __builtins__ , ' db ' : db . db , ' datetime ' : datetime }
new_globals . update ( extra_globals . __dict__ )
func = types . FunctionType ( code , globals = new_globals , name = k , argdefs = v . func_defaults )
func . __dict__ . update ( v . __dict__ )
setattr ( namespace , k , func )
# For all of the custom functions that have been added so far, give
# them access to the new function we've just created.
for new_k , new_v in new_functions . items ( ) :
new_v . func_globals [ k ] = func
new_functions [ k ] = func
2005-09-26 13:12:22 +08:00
# Calculate the module_name using a poor-man's pluralization.
get_module_name = lambda class_name : class_name . lower ( ) + ' s '
# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
get_verbose_name = lambda class_name : re . sub ( ' ([A-Z]) ' , ' \\ 1 ' , class_name ) . lower ( ) . strip ( )
2005-07-13 09:25:57 +08:00
class ModelBase ( type ) :
" Metaclass for all models "
def __new__ ( cls , name , bases , attrs ) :
# If this isn't a subclass of Model, don't do anything special.
if not bases :
return type . __new__ ( cls , name , bases , attrs )
2005-08-26 06:51:30 +08:00
try :
meta_attrs = attrs . pop ( ' META ' ) . __dict__
del meta_attrs [ ' __module__ ' ]
del meta_attrs [ ' __doc__ ' ]
except KeyError :
meta_attrs = { }
# Gather all attributes that are Field instances.
fields = [ ]
for obj_name , obj in attrs . items ( ) :
if isinstance ( obj , Field ) :
obj . set_name ( obj_name )
fields . append ( obj )
del attrs [ obj_name ]
# Sort the fields in the order that they were created. The
# "creation_counter" is needed because metaclasses don't preserve the
# attribute order.
fields . sort ( lambda x , y : x . creation_counter - y . creation_counter )
# If this model is a subclass of another model, create an Options
2005-07-13 09:25:57 +08:00
# object by first copying the base class's _meta and then updating it
# with the overrides from this class.
replaces_module = None
if bases [ 0 ] != Model :
2005-08-26 06:51:30 +08:00
field_names = [ f . name for f in fields ]
remove_fields = meta_attrs . pop ( ' remove_fields ' , [ ] )
for f in bases [ 0 ] . _meta . _orig_init_args [ ' fields ' ] :
if f . name not in field_names and f . name not in remove_fields :
fields . insert ( 0 , f )
if meta_attrs . has_key ( ' replaces_module ' ) :
2005-07-13 09:25:57 +08:00
# Set the replaces_module variable for now. We can't actually
# do anything with it yet, because the module hasn't yet been
# created.
2005-08-26 06:51:30 +08:00
replaces_module = meta_attrs . pop ( ' replaces_module ' ) . split ( ' . ' )
2005-07-13 09:25:57 +08:00
# Pass any Options overrides to the base's Options instance, and
# simultaneously remove them from attrs. When this is done, attrs
# will be a dictionary of custom methods, plus __module__.
2005-09-26 13:12:22 +08:00
meta_overrides = { ' fields ' : fields , ' module_name ' : get_module_name ( name ) , ' verbose_name ' : get_verbose_name ( name ) }
2005-08-26 06:51:30 +08:00
for k , v in meta_attrs . items ( ) :
2005-07-13 09:25:57 +08:00
if not callable ( v ) and k != ' __module__ ' :
2005-08-26 06:51:30 +08:00
meta_overrides [ k ] = meta_attrs . pop ( k )
2005-07-13 09:25:57 +08:00
opts = bases [ 0 ] . _meta . copy ( * * meta_overrides )
opts . object_name = name
del meta_overrides
else :
opts = Options (
2005-09-26 13:12:22 +08:00
module_name = meta_attrs . pop ( ' module_name ' , get_module_name ( name ) ) ,
2005-07-13 09:25:57 +08:00
# If the verbose_name wasn't given, use the class name,
# converted from InitialCaps to "lowercase with spaces".
2005-09-26 13:12:22 +08:00
verbose_name = meta_attrs . pop ( ' verbose_name ' , get_verbose_name ( name ) ) ,
2005-08-26 06:51:30 +08:00
verbose_name_plural = meta_attrs . pop ( ' verbose_name_plural ' , ' ' ) ,
db_table = meta_attrs . pop ( ' db_table ' , ' ' ) ,
fields = fields ,
ordering = meta_attrs . pop ( ' ordering ' , None ) ,
unique_together = meta_attrs . pop ( ' unique_together ' , None ) ,
admin = meta_attrs . pop ( ' admin ' , None ) ,
has_related_links = meta_attrs . pop ( ' has_related_links ' , False ) ,
where_constraints = meta_attrs . pop ( ' where_constraints ' , None ) ,
2005-07-13 09:25:57 +08:00
object_name = name ,
2005-08-26 06:51:30 +08:00
app_label = meta_attrs . pop ( ' app_label ' , None ) ,
exceptions = meta_attrs . pop ( ' exceptions ' , None ) ,
permissions = meta_attrs . pop ( ' permissions ' , None ) ,
get_latest_by = meta_attrs . pop ( ' get_latest_by ' , None ) ,
order_with_respect_to = meta_attrs . pop ( ' order_with_respect_to ' , None ) ,
module_constants = meta_attrs . pop ( ' module_constants ' , None ) ,
2005-07-13 09:25:57 +08:00
)
2005-08-26 06:51:30 +08:00
if meta_attrs != { } :
raise TypeError , " ' class META ' got invalid attribute(s): %s " % ' , ' . join ( meta_attrs . keys ( ) )
2005-07-13 09:25:57 +08:00
# Dynamically create the module that will contain this class and its
# associated helper functions.
if replaces_module is not None :
new_mod = get_module ( * replaces_module )
else :
new_mod = types . ModuleType ( opts . module_name )
# Collect any/all custom class methods and module functions, and move
# them to a temporary holding variable. We'll deal with them later.
if replaces_module is not None :
# Initialize these values to the base class' custom_methods and
# custom_functions.
custom_methods = dict ( [ ( k , v ) for k , v in new_mod . Klass . __dict__ . items ( ) if hasattr ( v , ' custom ' ) ] )
custom_functions = dict ( [ ( k , v ) for k , v in new_mod . __dict__ . items ( ) if hasattr ( v , ' custom ' ) ] )
else :
custom_methods , custom_functions = { } , { }
manipulator_methods = { }
for k , v in attrs . items ( ) :
if k in ( ' __module__ ' , ' __init__ ' , ' _overrides ' , ' __doc__ ' ) :
continue # Skip the important stuff.
2005-08-15 12:44:19 +08:00
assert callable ( v ) , " %r is an invalid model parameter. " % k
2005-07-13 09:25:57 +08:00
# Give the function a function attribute "custom" to designate that
# it's a custom function/method.
v . custom = True
if k . startswith ( MODEL_FUNCTIONS_PREFIX ) :
custom_functions [ k [ len ( MODEL_FUNCTIONS_PREFIX ) : ] ] = v
elif k . startswith ( MANIPULATOR_FUNCTIONS_PREFIX ) :
manipulator_methods [ k [ len ( MANIPULATOR_FUNCTIONS_PREFIX ) : ] ] = v
else :
custom_methods [ k ] = v
del attrs [ k ]
# Create the module-level ObjectDoesNotExist exception.
dne_exc_name = ' %s DoesNotExist ' % name
does_not_exist_exception = types . ClassType ( dne_exc_name , ( ObjectDoesNotExist , ) , { } )
# Explicitly set its __module__ because it will initially (incorrectly)
# be set to the module the code is being executed in.
does_not_exist_exception . __module__ = MODEL_PREFIX + ' . ' + opts . module_name
setattr ( new_mod , dne_exc_name , does_not_exist_exception )
# Create other exceptions.
for exception_name in opts . exceptions :
exc = types . ClassType ( exception_name , ( Exception , ) , { } )
exc . __module__ = MODEL_PREFIX + ' . ' + opts . module_name # Set this explicitly, as above.
setattr ( new_mod , exception_name , exc )
# Create any module-level constants, if applicable.
for k , v in opts . module_constants . items ( ) :
setattr ( new_mod , k , v )
# Create the default class methods.
attrs [ ' __init__ ' ] = curry ( method_init , opts )
attrs [ ' __eq__ ' ] = curry ( method_eq , opts )
attrs [ ' save ' ] = curry ( method_save , opts )
attrs [ ' save ' ] . alters_data = True
attrs [ ' delete ' ] = curry ( method_delete , opts )
attrs [ ' delete ' ] . alters_data = True
if opts . order_with_respect_to :
attrs [ ' get_next_in_order ' ] = curry ( method_get_next_in_order , opts , opts . order_with_respect_to )
attrs [ ' get_previous_in_order ' ] = curry ( method_get_previous_in_order , opts , opts . order_with_respect_to )
for f in opts . fields :
# If the object has a relationship to itself, as designated by
# RECURSIVE_RELATIONSHIP_CONSTANT, create that relationship formally.
if f . rel and f . rel . to == RECURSIVE_RELATIONSHIP_CONSTANT :
f . rel . to = opts
2005-08-26 06:51:30 +08:00
f . name = f . name or ( f . rel . to . object_name . lower ( ) + ' _ ' + f . rel . to . pk . name )
2005-07-20 13:46:00 +08:00
f . verbose_name = f . verbose_name or f . rel . to . verbose_name
f . rel . field_name = f . rel . field_name or f . rel . to . pk . name
2005-07-13 09:25:57 +08:00
# Add "get_thingie" methods for many-to-one related objects.
# EXAMPLES: Choice.get_poll(), Story.get_dateline()
if isinstance ( f . rel , ManyToOne ) :
func = curry ( method_get_many_to_one , f )
func . __doc__ = " Returns the associated ` %s . %s ` object. " % ( f . rel . to . app_label , f . rel . to . module_name )
2005-08-26 06:51:30 +08:00
attrs [ ' get_ %s ' % f . name ] = func
2005-07-13 09:25:57 +08:00
for f in opts . many_to_many :
# Add "get_thingie" methods for many-to-many related objects.
2005-07-14 11:17:09 +08:00
# EXAMPLES: Poll.get_site_list(), Story.get_byline_list()
2005-07-13 09:25:57 +08:00
func = curry ( method_get_many_to_many , f )
func . __doc__ = " Returns a list of associated ` %s . %s ` objects. " % ( f . rel . to . app_label , f . rel . to . module_name )
2005-08-26 06:51:30 +08:00
attrs [ ' get_ %s _list ' % f . rel . singular ] = func
2005-07-13 09:25:57 +08:00
# Add "set_thingie" methods for many-to-many related objects.
# EXAMPLES: Poll.set_sites(), Story.set_bylines()
func = curry ( method_set_many_to_many , f )
func . __doc__ = " Resets this object ' s ` %s . %s ` list to the given list of IDs. Note that it doesn ' t check whether the given IDs are valid. " % ( f . rel . to . app_label , f . rel . to . module_name )
func . alters_data = True
attrs [ ' set_ %s ' % f . name ] = func
# Create the class, because we need it to use in currying.
new_class = type . __new__ ( cls , name , bases , attrs )
# Give the class a docstring -- its definition.
2005-10-15 08:54:42 +08:00
if new_class . __doc__ is None :
new_class . __doc__ = " %s . %s ( %s ) " % ( opts . module_name , name , " , " . join ( [ f . name for f in opts . fields ] ) )
2005-07-13 09:25:57 +08:00
# Create the standard, module-level API helper functions such
# as get_object() and get_list().
new_mod . get_object = curry ( function_get_object , opts , new_class , does_not_exist_exception )
new_mod . get_object . __doc__ = " Returns the %s object matching the given parameters. " % name
new_mod . get_list = curry ( function_get_list , opts , new_class )
new_mod . get_list . __doc__ = " Returns a list of %s objects matching the given parameters. " % name
new_mod . get_iterator = curry ( function_get_iterator , opts , new_class )
new_mod . get_iterator . __doc__ = " Returns an iterator of %s objects matching the given parameters. " % name
2005-08-01 23:32:42 +08:00
new_mod . get_values = curry ( function_get_values , opts , new_class )
new_mod . get_values . __doc__ = " Returns a list of dictionaries matching the given parameters. "
new_mod . get_values_iterator = curry ( function_get_values_iterator , opts , new_class )
new_mod . get_values_iterator . __doc__ = " Returns an iterator of dictionaries matching the given parameters. "
2005-07-13 09:25:57 +08:00
new_mod . get_count = curry ( function_get_count , opts )
new_mod . get_count . __doc__ = " Returns the number of %s objects matching the given parameters. " % name
new_mod . _get_sql_clause = curry ( function_get_sql_clause , opts )
new_mod . get_in_bulk = curry ( function_get_in_bulk , opts , new_class )
new_mod . get_in_bulk . __doc__ = " Returns a dictionary of ID -> %s for the %s objects with IDs in the given id_list. " % ( name , name )
if opts . get_latest_by :
new_mod . get_latest = curry ( function_get_latest , opts , new_class , does_not_exist_exception )
for f in opts . fields :
2005-09-26 04:36:05 +08:00
if f . choices :
# Add "get_thingie_display" method to get human-readable value.
func = curry ( method_get_display_value , f )
setattr ( new_class , ' get_ %s _display ' % f . name , func )
2005-07-13 09:25:57 +08:00
if isinstance ( f , DateField ) or isinstance ( f , DateTimeField ) :
# Add "get_next_by_thingie" and "get_previous_by_thingie" methods
# for all DateFields and DateTimeFields that cannot be null.
# EXAMPLES: Poll.get_next_by_pub_date(), Poll.get_previous_by_pub_date()
if not f . null :
setattr ( new_class , ' get_next_by_ %s ' % f . name , curry ( method_get_next_or_previous , new_mod . get_object , f , True ) )
setattr ( new_class , ' get_previous_by_ %s ' % f . name , curry ( method_get_next_or_previous , new_mod . get_object , f , False ) )
# Add "get_thingie_list" for all DateFields and DateTimeFields.
# EXAMPLE: polls.get_pub_date_list()
func = curry ( function_get_date_list , opts , f )
func . __doc__ = " Returns a list of days, months or years (as datetime.datetime objects) in which %s objects are available. The first parameter ( ' kind ' ) must be one of ' year ' , ' month ' or ' day ' . " % name
setattr ( new_mod , ' get_ %s _list ' % f . name , func )
elif isinstance ( f , FileField ) :
setattr ( new_class , ' get_ %s _filename ' % f . name , curry ( method_get_file_filename , f ) )
setattr ( new_class , ' get_ %s _url ' % f . name , curry ( method_get_file_url , f ) )
setattr ( new_class , ' get_ %s _size ' % f . name , curry ( method_get_file_size , f ) )
func = curry ( method_save_file , f )
func . alters_data = True
setattr ( new_class , ' save_ %s _file ' % f . name , func )
if isinstance ( f , ImageField ) :
# Add get_BLAH_width and get_BLAH_height methods, but only
# if the image field doesn't have width and height cache
# fields.
if not f . width_field :
setattr ( new_class , ' get_ %s _width ' % f . name , curry ( method_get_image_width , f ) )
if not f . height_field :
setattr ( new_class , ' get_ %s _height ' % f . name , curry ( method_get_image_height , f ) )
# Add the class itself to the new module we've created.
new_mod . __dict__ [ name ] = new_class
# Add "Klass" -- a shortcut reference to the class.
new_mod . __dict__ [ ' Klass ' ] = new_class
# Add the Manipulators.
new_mod . __dict__ [ ' AddManipulator ' ] = get_manipulator ( opts , new_class , manipulator_methods , add = True )
new_mod . __dict__ [ ' ChangeManipulator ' ] = get_manipulator ( opts , new_class , manipulator_methods , change = True )
# Now that we have references to new_mod and new_class, we can add
# any/all extra class methods to the new class. Note that we could
# have just left the extra methods in attrs (above), but that would
# have meant that any code within the extra methods would *not* have
# access to module-level globals, such as get_list(), db, etc.
# In order to give these methods access to those globals, we have to
# deconstruct the method getting its raw "code" object, then recreating
# the function with a new "globals" dictionary.
#
# To complicate matters more, because each method is manually assigned
# a "globals" value, that "globals" value does NOT include the methods
# that haven't been created yet. For instance, if there are two custom
# methods, foo() and bar(), and foo() is created first, it won't have
# bar() within its globals(). This is a problem because sometimes
# custom methods/functions refer to other custom methods/functions. To
# solve this problem, we keep track of the new functions created (in
# the new_functions variable) and manually append each new function to
# the func_globals() of all previously-created functions. So, by the
# end of the loop, all functions will "know" about all the other
# functions.
_reassign_globals ( custom_methods , new_mod , new_class )
_reassign_globals ( custom_functions , new_mod , new_mod )
_reassign_globals ( manipulator_methods , new_mod , new_mod . __dict__ [ ' AddManipulator ' ] )
_reassign_globals ( manipulator_methods , new_mod , new_mod . __dict__ [ ' ChangeManipulator ' ] )
if hasattr ( new_class , ' get_absolute_url ' ) :
new_class . get_absolute_url = curry ( get_absolute_url , opts , new_class . get_absolute_url )
# Get a reference to the module the class is in, and dynamically add
# the new module to it.
app_package = sys . modules . get ( new_class . __module__ )
if replaces_module is not None :
app_label = replaces_module [ 0 ]
else :
app_package . __dict__ [ opts . module_name ] = new_mod
app_label = app_package . __name__ [ app_package . __name__ . rfind ( ' . ' ) + 1 : ]
# Populate the _MODELS member on the module the class is in.
# Example: django.models.polls will have a _MODELS member that will
# contain this list:
# [<class 'django.models.polls.Poll'>, <class 'django.models.polls.Choice'>]
# Don't do this if replaces_module is set.
app_package . __dict__ . setdefault ( ' _MODELS ' , [ ] ) . append ( new_class )
# Cache the app label.
opts . app_label = app_label
# If the db_table wasn't provided, use the app_label + module_name.
if not opts . db_table :
opts . db_table = " %s _ %s " % ( app_label , opts . module_name )
new_class . _meta = opts
# Set the __file__ attribute to the __file__ attribute of its package,
# because they're technically from the same file. Note: if we didn't
# set this, sys.modules would think this module was built-in.
try :
new_mod . __file__ = app_package . __file__
except AttributeError :
# 'module' object has no attribute '__file__', which means the
# class was probably being entered via the interactive interpreter.
pass
# Add the module's entry to sys.modules -- for instance,
# "django.models.polls.polls". Note that "django.models.polls" has already
# been added automatically.
sys . modules . setdefault ( ' %s . %s . %s ' % ( MODEL_PREFIX , app_label , opts . module_name ) , new_mod )
# If this module replaces another one, get a reference to the other
# module's parent, and replace the other module with the one we've just
# created.
if replaces_module is not None :
old_app = get_app ( replaces_module [ 0 ] )
setattr ( old_app , replaces_module [ 1 ] , new_mod )
for i , model in enumerate ( old_app . _MODELS ) :
if model . _meta . module_name == replaces_module [ 1 ] :
# Replace the appropriate member of the old app's _MODELS
# data structure.
old_app . _MODELS [ i ] = new_class
# Replace all relationships to the old class with
# relationships to the new one.
for rel_opts , rel_field in model . _meta . get_all_related_objects ( ) :
rel_field . rel . to = opts
for rel_opts , rel_field in model . _meta . get_all_related_many_to_many_objects ( ) :
rel_field . rel . to = opts
break
return new_class
class Model :
__metaclass__ = ModelBase
2005-07-16 07:48:54 +08:00
def __repr__ ( self ) :
return ' < %s object> ' % self . __class__ . __name__
2005-07-13 09:25:57 +08:00
############################################
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
############################################
# CORE METHODS #############################
def method_init ( opts , self , * args , * * kwargs ) :
2005-08-02 00:26:39 +08:00
if kwargs :
for f in opts . fields :
2005-08-26 06:51:30 +08:00
if isinstance ( f . rel , ManyToOne ) :
try :
# Assume object instance was passed in.
rel_obj = kwargs . pop ( f . name )
except KeyError :
try :
# Object instance wasn't passed in -- must be an ID.
val = kwargs . pop ( f . column )
except KeyError :
val = f . get_default ( )
else :
# Special case: You can pass in "None" for related objects if it's allowed.
if rel_obj is None and f . null :
val = None
else :
try :
val = getattr ( rel_obj , f . rel . field_name )
except AttributeError :
raise TypeError , " Invalid value: %r should be a %s instance, not a %s " % ( f . name , f . rel . to , type ( rel_obj ) )
setattr ( self , f . column , val )
else :
val = kwargs . pop ( f . name , f . get_default ( ) )
setattr ( self , f . name , val )
2005-08-02 00:26:39 +08:00
if kwargs :
raise TypeError , " ' %s ' is an invalid keyword argument for this function " % kwargs . keys ( ) [ 0 ]
2005-07-13 09:25:57 +08:00
for i , arg in enumerate ( args ) :
2005-08-26 06:51:30 +08:00
setattr ( self , opts . fields [ i ] . column , arg )
2005-07-13 09:25:57 +08:00
def method_eq ( opts , self , other ) :
2005-08-26 06:51:30 +08:00
return isinstance ( other , self . __class__ ) and getattr ( self , opts . pk . column ) == getattr ( other , opts . pk . column )
2005-07-13 09:25:57 +08:00
def method_save ( opts , self ) :
# Run any pre-save hooks.
if hasattr ( self , ' _pre_save ' ) :
self . _pre_save ( )
non_pks = [ f for f in opts . fields if not f . primary_key ]
cursor = db . db . cursor ( )
2005-08-10 13:08:27 +08:00
# First, try an UPDATE. If that doesn't update anything, do an INSERT.
2005-08-26 06:51:30 +08:00
pk_val = getattr ( self , opts . pk . column )
2005-08-16 00:00:28 +08:00
pk_set = bool ( pk_val )
record_exists = True
2005-08-10 13:08:27 +08:00
if pk_set :
2005-08-16 00:00:28 +08:00
# Determine whether a record with the primary key already exists.
2005-08-26 06:51:30 +08:00
cursor . execute ( " SELECT 1 FROM %s WHERE %s = %% s LIMIT 1 " % ( opts . db_table , opts . pk . column ) , [ pk_val ] )
2005-08-16 00:00:28 +08:00
# If it does already exist, do an UPDATE.
2005-08-16 04:03:34 +08:00
if cursor . fetchone ( ) :
2005-08-26 06:51:30 +08:00
db_values = [ f . get_db_prep_save ( f . pre_save ( getattr ( self , f . column ) , False ) ) for f in non_pks ]
2005-08-16 00:00:28 +08:00
cursor . execute ( " UPDATE %s SET %s WHERE %s = %% s " % ( opts . db_table ,
2005-08-26 06:51:30 +08:00
' , ' . join ( [ ' %s = %% s ' % f . column for f in non_pks ] ) , opts . pk . column ) ,
2005-08-16 00:00:28 +08:00
db_values + [ pk_val ] )
else :
record_exists = False
if not pk_set or not record_exists :
2005-08-26 06:51:30 +08:00
field_names = [ f . column for f in opts . fields if not isinstance ( f , AutoField ) ]
2005-08-10 13:08:27 +08:00
placeholders = [ ' %s ' ] * len ( field_names )
2005-08-26 06:51:30 +08:00
db_values = [ f . get_db_prep_save ( f . pre_save ( getattr ( self , f . column ) , True ) ) for f in opts . fields if not isinstance ( f , AutoField ) ]
2005-08-10 13:08:27 +08:00
if opts . order_with_respect_to :
field_names . append ( ' _order ' )
# TODO: This assumes the database supports subqueries.
placeholders . append ( ' (SELECT COUNT(*) FROM %s WHERE %s = %% s) ' % \
2005-08-26 06:51:30 +08:00
( opts . db_table , opts . order_with_respect_to . column ) )
db_values . append ( getattr ( self , opts . order_with_respect_to . column ) )
2005-08-10 13:08:27 +08:00
cursor . execute ( " INSERT INTO %s ( %s ) VALUES ( %s ) " % ( opts . db_table ,
' , ' . join ( field_names ) , ' , ' . join ( placeholders ) ) , db_values )
if opts . has_auto_field :
2005-08-26 06:51:30 +08:00
setattr ( self , opts . pk . column , db . get_last_insert_id ( cursor , opts . db_table , opts . pk . column ) )
2005-07-13 09:25:57 +08:00
db . db . commit ( )
# Run any post-save hooks.
if hasattr ( self , ' _post_save ' ) :
self . _post_save ( )
def method_delete ( opts , self ) :
2005-08-26 06:51:30 +08:00
assert getattr ( self , opts . pk . column ) is not None , " %r can ' t be deleted because it doesn ' t have an ID. "
2005-08-09 02:30:07 +08:00
# Run any pre-delete hooks.
if hasattr ( self , ' _pre_delete ' ) :
self . _pre_delete ( )
2005-07-13 09:25:57 +08:00
cursor = db . db . cursor ( )
for rel_opts , rel_field in opts . get_all_related_objects ( ) :
rel_opts_name = opts . get_rel_object_method_name ( rel_opts , rel_field )
if isinstance ( rel_field . rel , OneToOne ) :
try :
sub_obj = getattr ( self , ' get_ %s ' % rel_opts_name ) ( )
except ObjectDoesNotExist :
pass
else :
sub_obj . delete ( )
else :
for sub_obj in getattr ( self , ' get_ %s _list ' % rel_opts_name ) ( ) :
sub_obj . delete ( )
for rel_opts , rel_field in opts . get_all_related_many_to_many_objects ( ) :
cursor . execute ( " DELETE FROM %s WHERE %s _id= %% s " % ( rel_field . get_m2m_db_table ( rel_opts ) ,
2005-08-26 06:51:30 +08:00
self . _meta . object_name . lower ( ) ) , [ getattr ( self , opts . pk . column ) ] )
2005-08-10 08:21:41 +08:00
for f in opts . many_to_many :
cursor . execute ( " DELETE FROM %s WHERE %s _id= %% s " % ( f . get_m2m_db_table ( opts ) , self . _meta . object_name . lower ( ) ) ,
2005-08-26 06:51:30 +08:00
[ getattr ( self , opts . pk . column ) ] )
cursor . execute ( " DELETE FROM %s WHERE %s = %% s " % ( opts . db_table , opts . pk . column ) , [ getattr ( self , opts . pk . column ) ] )
2005-07-13 09:25:57 +08:00
db . db . commit ( )
2005-08-26 06:51:30 +08:00
setattr ( self , opts . pk . column , None )
2005-07-13 09:25:57 +08:00
for f in opts . fields :
2005-08-26 06:51:30 +08:00
if isinstance ( f , FileField ) and getattr ( self , f . column ) :
2005-07-13 09:25:57 +08:00
file_name = getattr ( self , ' get_ %s _filename ' % f . name ) ( )
# If the file exists and no other object of this type references it,
# delete it from the filesystem.
if os . path . exists ( file_name ) and not opts . get_model_module ( ) . get_list ( * * { ' %s __exact ' % f . name : getattr ( self , f . name ) } ) :
os . remove ( file_name )
2005-08-09 02:30:07 +08:00
# Run any post-delete hooks.
if hasattr ( self , ' _post_delete ' ) :
self . _post_delete ( )
2005-07-13 09:25:57 +08:00
def method_get_next_in_order ( opts , order_field , self ) :
if not hasattr ( self , ' _next_in_order_cache ' ) :
2005-07-22 21:02:27 +08:00
self . _next_in_order_cache = opts . get_model_module ( ) . get_object ( order_by = ( ' _order ' , ) ,
2005-08-26 06:51:30 +08:00
where = [ ' _order > (SELECT _order FROM %s WHERE %s = %% s) ' % ( opts . db_table , opts . pk . column ) ,
' %s = %% s ' % order_field . column ] , limit = 1 ,
2005-08-29 23:35:04 +08:00
params = [ getattr ( self , opts . pk . column ) , getattr ( self , order_field . column ) ] )
2005-07-13 09:25:57 +08:00
return self . _next_in_order_cache
def method_get_previous_in_order ( opts , order_field , self ) :
if not hasattr ( self , ' _previous_in_order_cache ' ) :
2005-07-22 21:02:27 +08:00
self . _previous_in_order_cache = opts . get_model_module ( ) . get_object ( order_by = ( ' -_order ' , ) ,
2005-08-26 06:51:30 +08:00
where = [ ' _order < (SELECT _order FROM %s WHERE %s = %% s) ' % ( opts . db_table , opts . pk . column ) ,
' %s = %% s ' % order_field . column ] , limit = 1 ,
2005-08-29 23:35:04 +08:00
params = [ getattr ( self , opts . pk . column ) , getattr ( self , order_field . column ) ] )
2005-07-13 09:25:57 +08:00
return self . _previous_in_order_cache
# RELATIONSHIP METHODS #####################
# Example: Story.get_dateline()
def method_get_many_to_one ( field_with_rel , self ) :
2005-08-26 06:51:30 +08:00
cache_var = field_with_rel . get_cache_name ( )
2005-07-13 09:25:57 +08:00
if not hasattr ( self , cache_var ) :
2005-08-26 06:51:30 +08:00
val = getattr ( self , field_with_rel . column )
2005-07-13 09:25:57 +08:00
mod = field_with_rel . rel . to . get_model_module ( )
if val is None :
raise getattr ( mod , ' %s DoesNotExist ' % field_with_rel . rel . to . object_name )
retrieved_obj = mod . get_object ( * * { ' %s __exact ' % field_with_rel . rel . field_name : val } )
setattr ( self , cache_var , retrieved_obj )
return getattr ( self , cache_var )
# Handles getting many-to-many related objects.
2005-07-14 11:17:09 +08:00
# Example: Poll.get_site_list()
2005-07-13 09:25:57 +08:00
def method_get_many_to_many ( field_with_rel , self ) :
rel = field_with_rel . rel . to
cache_var = ' _ %s _cache ' % field_with_rel . name
if not hasattr ( self , cache_var ) :
mod = rel . get_model_module ( )
sql = " SELECT %s FROM %s a, %s b WHERE a. %s = b. %s _id AND b. %s _id = %% s %s " % \
2005-08-26 06:51:30 +08:00
( ' , ' . join ( [ ' a. %s ' % f . column for f in rel . fields ] ) , rel . db_table ,
field_with_rel . get_m2m_db_table ( self . _meta ) , rel . pk . column ,
2005-07-13 09:25:57 +08:00
rel . object_name . lower ( ) , self . _meta . object_name . lower ( ) , rel . get_order_sql ( ' a ' ) )
cursor = db . db . cursor ( )
2005-08-26 06:51:30 +08:00
cursor . execute ( sql , [ getattr ( self , self . _meta . pk . column ) ] )
2005-07-13 09:25:57 +08:00
setattr ( self , cache_var , [ getattr ( mod , rel . object_name ) ( * row ) for row in cursor . fetchall ( ) ] )
return getattr ( self , cache_var )
# Handles setting many-to-many relationships.
# Example: Poll.set_sites()
def method_set_many_to_many ( rel_field , self , id_list ) :
current_ids = [ obj . id for obj in method_get_many_to_many ( rel_field , self ) ]
ids_to_add , ids_to_delete = dict ( [ ( i , 1 ) for i in id_list ] ) , [ ]
for current_id in current_ids :
if current_id in id_list :
del ids_to_add [ current_id ]
else :
ids_to_delete . append ( current_id )
ids_to_add = ids_to_add . keys ( )
# Now ids_to_add is a list of IDs to add, and ids_to_delete is a list of IDs to delete.
if not ids_to_delete and not ids_to_add :
return False # No change
rel = rel_field . rel . to
m2m_table = rel_field . get_m2m_db_table ( self . _meta )
cursor = db . db . cursor ( )
2005-08-26 06:51:30 +08:00
this_id = getattr ( self , self . _meta . pk . column )
2005-07-13 09:25:57 +08:00
if ids_to_delete :
sql = " DELETE FROM %s WHERE %s _id = %% s AND %s _id IN ( %s ) " % ( m2m_table , self . _meta . object_name . lower ( ) , rel . object_name . lower ( ) , ' , ' . join ( map ( str , ids_to_delete ) ) )
cursor . execute ( sql , [ this_id ] )
if ids_to_add :
sql = " INSERT INTO %s ( %s _id, %s _id) VALUES ( %% s, %% s) " % ( m2m_table , self . _meta . object_name . lower ( ) , rel . object_name . lower ( ) )
cursor . executemany ( sql , [ ( this_id , i ) for i in ids_to_add ] )
db . db . commit ( )
try :
delattr ( self , ' _ %s _cache ' % rel_field . name ) # clear cache, if it exists
except AttributeError :
pass
return True
# Handles related-object retrieval.
# Examples: Poll.get_choice(), Poll.get_choice_list(), Poll.get_choice_count()
def method_get_related ( method_name , rel_mod , rel_field , self , * * kwargs ) :
2005-09-03 01:59:43 +08:00
if self . _meta . has_related_links and rel_mod . Klass . _meta . module_name == ' relatedlinks ' :
kwargs [ ' object_id__exact ' ] = getattr ( self , rel_field . rel . field_name )
else :
kwargs [ ' %s __ %s __exact ' % ( rel_field . name , rel_field . rel . to . pk . name ) ] = getattr ( self , rel_field . rel . field_name )
2005-07-13 09:25:57 +08:00
kwargs . update ( rel_field . rel . lookup_overrides )
return getattr ( rel_mod , method_name ) ( * * kwargs )
# Handles adding related objects.
# Example: Poll.add_choice()
def method_add_related ( rel_obj , rel_mod , rel_field , self , * args , * * kwargs ) :
init_kwargs = dict ( zip ( [ f . name for f in rel_obj . fields if f != rel_field and not isinstance ( f , AutoField ) ] , args ) )
init_kwargs . update ( kwargs )
for f in rel_obj . fields :
if isinstance ( f , AutoField ) :
init_kwargs [ f . name ] = None
2005-08-26 06:51:30 +08:00
init_kwargs [ rel_field . name ] = self
2005-07-13 09:25:57 +08:00
obj = rel_mod . Klass ( * * init_kwargs )
obj . save ( )
return obj
# Handles related many-to-many object retrieval.
# Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count()
2005-09-13 11:48:06 +08:00
def method_get_related_many_to_many ( method_name , opts , rel_mod , rel_field , self , * * kwargs ) :
kwargs [ ' %s __ %s __exact ' % ( rel_field . name , opts . pk . name ) ] = getattr ( self , opts . pk . column )
2005-07-13 09:25:57 +08:00
return getattr ( rel_mod , method_name ) ( * * kwargs )
# Handles setting many-to-many related objects.
# Example: Album.set_songs()
def method_set_related_many_to_many ( rel_opts , rel_field , self , id_list ) :
id_list = map ( int , id_list ) # normalize to integers
rel = rel_field . rel . to
m2m_table = rel_field . get_m2m_db_table ( rel_opts )
2005-08-26 06:51:30 +08:00
this_id = getattr ( self , self . _meta . pk . column )
2005-07-13 09:25:57 +08:00
cursor = db . db . cursor ( )
cursor . execute ( " DELETE FROM %s WHERE %s _id = %% s " % ( m2m_table , rel . object_name . lower ( ) ) , [ this_id ] )
2005-07-18 01:27:34 +08:00
sql = " INSERT INTO %s ( %s _id, %s _id) VALUES ( %% s, %% s) " % ( m2m_table , rel . object_name . lower ( ) , rel_opts . object_name . lower ( ) )
cursor . executemany ( sql , [ ( this_id , i ) for i in id_list ] )
2005-07-13 09:25:57 +08:00
db . db . commit ( )
# ORDERING METHODS #########################
def method_set_order ( ordered_obj , self , id_list ) :
cursor = db . db . cursor ( )
# Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
2005-08-26 06:51:30 +08:00
sql = " UPDATE %s SET _order = %% s WHERE %s = %% s AND %s = %% s " % ( ordered_obj . db_table , ordered_obj . order_with_respect_to . column , ordered_obj . pk . column )
2005-07-13 09:25:57 +08:00
rel_val = getattr ( self , ordered_obj . order_with_respect_to . rel . field_name )
cursor . executemany ( sql , [ ( i , rel_val , j ) for i , j in enumerate ( id_list ) ] )
db . db . commit ( )
def method_get_order ( ordered_obj , self ) :
cursor = db . db . cursor ( )
# Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
2005-08-26 06:51:30 +08:00
sql = " SELECT %s FROM %s WHERE %s = %% s ORDER BY _order " % ( ordered_obj . pk . column , ordered_obj . db_table , ordered_obj . order_with_respect_to . column )
2005-07-13 09:25:57 +08:00
rel_val = getattr ( self , ordered_obj . order_with_respect_to . rel . field_name )
cursor . execute ( sql , [ rel_val ] )
return [ r [ 0 ] for r in cursor . fetchall ( ) ]
# DATE-RELATED METHODS #####################
def method_get_next_or_previous ( get_object_func , field , is_next , self , * * kwargs ) :
2005-08-26 06:51:30 +08:00
kwargs . setdefault ( ' where ' , [ ] ) . append ( ' %s %s %% s ' % ( field . column , ( is_next and ' > ' or ' < ' ) ) )
2005-07-13 09:25:57 +08:00
kwargs . setdefault ( ' params ' , [ ] ) . append ( str ( getattr ( self , field . name ) ) )
2005-07-22 21:02:27 +08:00
kwargs [ ' order_by ' ] = [ ( not is_next and ' - ' or ' ' ) + field . name ]
2005-07-13 09:25:57 +08:00
kwargs [ ' limit ' ] = 1
return get_object_func ( * * kwargs )
2005-09-26 04:36:05 +08:00
# CHOICE-RELATED METHODS ###################
def method_get_display_value ( field , self ) :
value = getattr ( self , field . column )
return dict ( field . choices ) . get ( value , value )
2005-07-13 09:25:57 +08:00
# FILE-RELATED METHODS #####################
def method_get_file_filename ( field , self ) :
return os . path . join ( settings . MEDIA_ROOT , getattr ( self , field . name ) )
def method_get_file_url ( field , self ) :
if getattr ( self , field . name ) : # value is not blank
import urlparse
2005-09-01 00:22:07 +08:00
return urlparse . urljoin ( settings . MEDIA_URL , getattr ( self , field . name ) ) . replace ( ' \\ ' , ' / ' )
2005-07-13 09:25:57 +08:00
return ' '
def method_get_file_size ( field , self ) :
return os . path . getsize ( method_get_file_filename ( field , self ) )
def method_save_file ( field , self , filename , raw_contents ) :
directory = field . get_directory_name ( )
try : # Create the date-based directory if it doesn't exist.
os . makedirs ( os . path . join ( settings . MEDIA_ROOT , directory ) )
except OSError : # Directory probably already exists.
pass
filename = field . get_filename ( filename )
# If the filename already exists, keep adding an underscore to the name of
# the file until the filename doesn't exist.
while os . path . exists ( os . path . join ( settings . MEDIA_ROOT , filename ) ) :
try :
dot_index = filename . rindex ( ' . ' )
except ValueError : # filename has no dot
filename + = ' _ '
else :
filename = filename [ : dot_index ] + ' _ ' + filename [ dot_index : ]
# Write the file to disk.
setattr ( self , field . name , filename )
2005-07-26 06:17:47 +08:00
fp = open ( getattr ( self , ' get_ %s _filename ' % field . name ) ( ) , ' wb ' )
2005-07-13 09:25:57 +08:00
fp . write ( raw_contents )
fp . close ( )
# Save the width and/or height, if applicable.
if isinstance ( field , ImageField ) and ( field . width_field or field . height_field ) :
from django . utils . images import get_image_dimensions
width , height = get_image_dimensions ( getattr ( self , ' get_ %s _filename ' % field . name ) ( ) )
if field . width_field :
setattr ( self , field . width_field , width )
if field . height_field :
setattr ( self , field . height_field , height )
# Save the object, because it has changed.
self . save ( )
# IMAGE FIELD METHODS ######################
def method_get_image_width ( field , self ) :
return _get_image_dimensions ( field , self ) [ 0 ]
def method_get_image_height ( field , self ) :
return _get_image_dimensions ( field , self ) [ 1 ]
def _get_image_dimensions ( field , self ) :
cachename = " __ %s _dimensions_cache " % field . name
if not hasattr ( self , cachename ) :
from django . utils . images import get_image_dimensions
fname = getattr ( self , " get_ %s _filename " % field . name ) ( )
setattr ( self , cachename , get_image_dimensions ( fname ) )
return getattr ( self , cachename )
##############################################
# HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
##############################################
def get_absolute_url ( opts , func , self ) :
return settings . ABSOLUTE_URL_OVERRIDES . get ( ' %s . %s ' % ( opts . app_label , opts . module_name ) , func ) ( self )
def _get_where_clause ( lookup_type , table_prefix , field_name , value ) :
try :
return ' %s %s %s %% s ' % ( table_prefix , field_name , db . OPERATOR_MAPPING [ lookup_type ] )
except KeyError :
pass
2005-07-27 00:53:58 +08:00
if lookup_type == ' in ' :
return ' %s %s IN ( %s ) ' % ( table_prefix , field_name , ' , ' . join ( [ ' %s ' for v in value ] ) )
elif lookup_type in ( ' range ' , ' year ' ) :
2005-07-13 09:25:57 +08:00
return ' %s %s BETWEEN %% s AND %% s ' % ( table_prefix , field_name )
elif lookup_type in ( ' month ' , ' day ' ) :
2005-07-18 02:23:34 +08:00
return " %s = %% s " % db . get_date_extract_sql ( lookup_type , table_prefix + field_name )
2005-07-13 09:25:57 +08:00
elif lookup_type == ' isnull ' :
return " %s %s IS %s NULL " % ( table_prefix , field_name , ( not value and ' NOT ' or ' ' ) )
raise TypeError , " Got invalid lookup_type: %s " % repr ( lookup_type )
def function_get_object ( opts , klass , does_not_exist_exception , * * kwargs ) :
obj_list = function_get_list ( opts , klass , * * kwargs )
if len ( obj_list ) < 1 :
raise does_not_exist_exception , " %s does not exist for %s " % ( opts . object_name , kwargs )
assert len ( obj_list ) == 1 , " get_object() returned more than one %s -- it returned %s ! Lookup parameters were %s " % ( opts . object_name , len ( obj_list ) , kwargs )
return obj_list [ 0 ]
def _get_cached_row ( opts , row , index_start ) :
" Helper function that recursively returns an object with cache filled "
index_end = index_start + len ( opts . fields )
obj = opts . get_model_module ( ) . Klass ( * row [ index_start : index_end ] )
for f in opts . fields :
if f . rel and not f . null :
rel_obj , index_end = _get_cached_row ( f . rel . to , row , index_end )
2005-08-26 06:51:30 +08:00
setattr ( obj , f . get_cache_name ( ) , rel_obj )
2005-07-13 09:25:57 +08:00
return obj , index_end
def function_get_iterator ( opts , klass , * * kwargs ) :
# kwargs['select'] is a dictionary, and dictionaries' key order is
# undefined, so we convert it to a list of tuples internally.
kwargs [ ' select ' ] = kwargs . get ( ' select ' , { } ) . items ( )
cursor = db . db . cursor ( )
select , sql , params = function_get_sql_clause ( opts , * * kwargs )
cursor . execute ( " SELECT " + ( kwargs . get ( ' distinct ' ) and " DISTINCT " or " " ) + " , " . join ( select ) + sql , params )
fill_cache = kwargs . get ( ' select_related ' )
index_end = len ( opts . fields )
while 1 :
rows = cursor . fetchmany ( GET_ITERATOR_CHUNK_SIZE )
if not rows :
raise StopIteration
for row in rows :
if fill_cache :
obj , index_end = _get_cached_row ( opts , row , 0 )
else :
obj = klass ( * row [ : index_end ] )
for i , k in enumerate ( kwargs [ ' select ' ] ) :
setattr ( obj , k [ 0 ] , row [ index_end + i ] )
yield obj
2005-07-27 07:04:50 +08:00
def function_get_list ( opts , klass , * * kwargs ) :
return list ( function_get_iterator ( opts , klass , * * kwargs ) )
2005-07-13 09:25:57 +08:00
def function_get_count ( opts , * * kwargs ) :
kwargs [ ' order_by ' ] = [ ]
kwargs [ ' offset ' ] = None
kwargs [ ' limit ' ] = None
kwargs [ ' select_related ' ] = False
_ , sql , params = function_get_sql_clause ( opts , * * kwargs )
cursor = db . db . cursor ( )
cursor . execute ( " SELECT COUNT(*) " + sql , params )
return cursor . fetchone ( ) [ 0 ]
2005-08-01 23:32:42 +08:00
def function_get_values_iterator ( opts , klass , * * kwargs ) :
# select_related and select aren't supported in get_values().
kwargs [ ' select_related ' ] = False
kwargs [ ' select ' ] = { }
# 'fields' is a list of field names to fetch.
try :
2005-08-26 06:51:30 +08:00
fields = [ opts . get_field ( f ) . column for f in kwargs . pop ( ' fields ' ) ]
2005-08-01 23:32:42 +08:00
except KeyError : # Default to all fields.
2005-08-26 06:51:30 +08:00
fields = [ f . column for f in opts . fields ]
2005-08-01 23:32:42 +08:00
cursor = db . db . cursor ( )
_ , sql , params = function_get_sql_clause ( opts , * * kwargs )
select = [ ' %s . %s ' % ( opts . db_table , f ) for f in fields ]
cursor . execute ( " SELECT " + ( kwargs . get ( ' distinct ' ) and " DISTINCT " or " " ) + " , " . join ( select ) + sql , params )
while 1 :
rows = cursor . fetchmany ( GET_ITERATOR_CHUNK_SIZE )
if not rows :
raise StopIteration
for row in rows :
yield dict ( zip ( fields , row ) )
def function_get_values ( opts , klass , * * kwargs ) :
return list ( function_get_values_iterator ( opts , klass , * * kwargs ) )
2005-07-13 09:25:57 +08:00
def _fill_table_cache ( opts , select , tables , where , old_prefix , cache_tables_seen ) :
"""
Helper function that recursively populates the select , tables and where ( in
place ) for fill - cache queries .
"""
for f in opts . fields :
if f . rel and not f . null :
db_table = f . rel . to . db_table
if db_table not in cache_tables_seen :
tables . append ( db_table )
else : # The table was already seen, so give it a table alias.
new_prefix = ' %s %s ' % ( db_table , len ( cache_tables_seen ) )
tables . append ( ' %s %s ' % ( db_table , new_prefix ) )
db_table = new_prefix
cache_tables_seen . append ( db_table )
2005-08-26 06:51:30 +08:00
where . append ( ' %s . %s = %s . %s ' % ( old_prefix , f . column , db_table , f . rel . get_related_field ( ) . column ) )
select . extend ( [ ' %s . %s ' % ( db_table , f2 . column ) for f2 in f . rel . to . fields ] )
2005-07-13 09:25:57 +08:00
_fill_table_cache ( f . rel . to , select , tables , where , db_table , cache_tables_seen )
def _throw_bad_kwarg_error ( kwarg ) :
# Helper function to remove redundancy.
raise TypeError , " got unexpected keyword argument ' %s ' " % kwarg
def _parse_lookup ( kwarg_items , opts , table_count = 0 ) :
# Helper function that handles converting API kwargs (e.g.
# "name__exact": "tom") to SQL.
# Note that there is a distinction between where and join_where. The latter
# is specifically a list of where clauses to use for JOINs. This
# distinction is necessary because of support for "_or".
# table_count is used to ensure table aliases are unique.
tables , join_where , where , params = [ ] , [ ] , [ ] , [ ]
for kwarg , kwarg_value in kwarg_items :
if kwarg in ( ' order_by ' , ' limit ' , ' offset ' , ' select_related ' , ' distinct ' , ' select ' , ' tables ' , ' where ' , ' params ' ) :
continue
if kwarg_value is None :
continue
if kwarg == ' _or ' :
for val in kwarg_value :
tables2 , join_where2 , where2 , params2 , table_count = _parse_lookup ( val , opts , table_count )
tables . extend ( tables2 )
join_where . extend ( join_where2 )
where . append ( ' ( %s ) ' % ' OR ' . join ( where2 ) )
params . extend ( params2 )
continue
lookup_list = kwarg . split ( LOOKUP_SEPARATOR )
2005-07-27 00:11:43 +08:00
# pk="value" is shorthand for (primary key)__exact="value"
if lookup_list [ - 1 ] == ' pk ' :
2005-08-26 06:51:30 +08:00
if opts . pk . rel :
lookup_list = lookup_list [ : - 1 ] + [ opts . pk . name , opts . pk . rel . field_name , ' exact ' ]
else :
lookup_list = lookup_list [ : - 1 ] + [ opts . pk . name , ' exact ' ]
2005-07-13 09:25:57 +08:00
if len ( lookup_list ) == 1 :
_throw_bad_kwarg_error ( kwarg )
lookup_type = lookup_list . pop ( )
current_opts = opts # We'll be overwriting this, so keep a reference to the original opts.
current_table_alias = current_opts . db_table
param_required = False
while lookup_list or param_required :
table_count + = 1
try :
# "current" is a piece of the lookup list. For example, in
# choices.get_list(poll__sites__id__exact=5), lookup_list is
# ["polls", "sites", "id"], and the first current is "polls".
try :
current = lookup_list . pop ( 0 )
except IndexError :
# If we're here, lookup_list is empty but param_required
# is set to True, which means the kwarg was bad.
# Example: choices.get_list(poll__exact='foo')
_throw_bad_kwarg_error ( kwarg )
# Try many-to-many relationships first...
for f in current_opts . many_to_many :
if f . name == current :
rel_table_alias = ' t %s ' % table_count
table_count + = 1
tables . append ( ' %s %s ' % ( f . get_m2m_db_table ( current_opts ) , rel_table_alias ) )
2005-08-26 06:51:30 +08:00
join_where . append ( ' %s . %s = %s . %s _id ' % ( current_table_alias , current_opts . pk . column ,
2005-07-13 09:25:57 +08:00
rel_table_alias , current_opts . object_name . lower ( ) ) )
# Optimization: In the case of primary-key lookups, we
# don't have to do an extra join.
if lookup_list and lookup_list [ 0 ] == f . rel . to . pk . name and lookup_type == ' exact ' :
where . append ( _get_where_clause ( lookup_type , rel_table_alias + ' . ' ,
f . rel . to . object_name . lower ( ) + ' _id ' , kwarg_value ) )
params . extend ( f . get_db_prep_lookup ( lookup_type , kwarg_value ) )
lookup_list . pop ( )
param_required = False
else :
new_table_alias = ' t %s ' % table_count
tables . append ( ' %s %s ' % ( f . rel . to . db_table , new_table_alias ) )
join_where . append ( ' %s . %s _id = %s . %s ' % ( rel_table_alias , f . rel . to . object_name . lower ( ) ,
2005-08-26 06:51:30 +08:00
new_table_alias , f . rel . to . pk . column ) )
2005-07-13 09:25:57 +08:00
current_table_alias = new_table_alias
param_required = True
current_opts = f . rel . to
raise StopIteration
for f in current_opts . fields :
# Try many-to-one relationships...
2005-08-26 06:51:30 +08:00
if f . rel and f . name == current :
2005-07-13 09:25:57 +08:00
# Optimization: In the case of primary-key lookups, we
# don't have to do an extra join.
if lookup_list and lookup_list [ 0 ] == f . rel . to . pk . name and lookup_type == ' exact ' :
2005-08-26 06:51:30 +08:00
where . append ( _get_where_clause ( lookup_type , current_table_alias + ' . ' , f . column , kwarg_value ) )
2005-07-13 09:25:57 +08:00
params . extend ( f . get_db_prep_lookup ( lookup_type , kwarg_value ) )
lookup_list . pop ( )
param_required = False
2005-08-26 06:51:30 +08:00
# 'isnull' lookups in many-to-one relationships are a special case,
# because we don't want to do a join. We just want to find out
# whether the foreign key field is NULL.
elif lookup_type == ' isnull ' and not lookup_list :
where . append ( _get_where_clause ( lookup_type , current_table_alias + ' . ' , f . column , kwarg_value ) )
params . extend ( f . get_db_prep_lookup ( lookup_type , kwarg_value ) )
2005-07-13 09:25:57 +08:00
else :
new_table_alias = ' t %s ' % table_count
tables . append ( ' %s %s ' % ( f . rel . to . db_table , new_table_alias ) )
2005-08-26 06:51:30 +08:00
join_where . append ( ' %s . %s = %s . %s ' % ( current_table_alias , f . column , \
new_table_alias , f . rel . to . pk . column ) )
2005-07-13 09:25:57 +08:00
current_table_alias = new_table_alias
param_required = True
current_opts = f . rel . to
raise StopIteration
# Try direct field-name lookups...
if f . name == current :
2005-08-26 06:51:30 +08:00
where . append ( _get_where_clause ( lookup_type , current_table_alias + ' . ' , f . column , kwarg_value ) )
2005-07-13 09:25:57 +08:00
params . extend ( f . get_db_prep_lookup ( lookup_type , kwarg_value ) )
param_required = False
raise StopIteration
# If we haven't hit StopIteration at this point, "current" must be
# an invalid lookup, so raise an exception.
_throw_bad_kwarg_error ( kwarg )
except StopIteration :
continue
return tables , join_where , where , params , table_count
def function_get_sql_clause ( opts , * * kwargs ) :
2005-08-26 06:51:30 +08:00
select = [ " %s . %s " % ( opts . db_table , f . column ) for f in opts . fields ]
2005-07-13 09:25:57 +08:00
tables = [ opts . db_table ] + ( kwargs . get ( ' tables ' ) and kwargs [ ' tables ' ] [ : ] or [ ] )
where = kwargs . get ( ' where ' ) and kwargs [ ' where ' ] [ : ] or [ ]
params = kwargs . get ( ' params ' ) and kwargs [ ' params ' ] [ : ] or [ ]
# Convert the kwargs into SQL.
tables2 , join_where2 , where2 , params2 , _ = _parse_lookup ( kwargs . items ( ) , opts )
tables . extend ( tables2 )
where . extend ( join_where2 + where2 )
params . extend ( params2 )
# Add any additional constraints from the "where_constraints" parameter.
where . extend ( opts . where_constraints )
# Add additional tables and WHERE clauses based on select_related.
if kwargs . get ( ' select_related ' ) is True :
_fill_table_cache ( opts , select , tables , where , opts . db_table , [ opts . db_table ] )
# Add any additional SELECTs passed in via kwargs.
2005-08-01 23:32:42 +08:00
if kwargs . get ( ' select ' ) :
2005-07-13 09:25:57 +08:00
select . extend ( [ ' ( %s ) AS %s ' % ( s [ 1 ] , s [ 0 ] ) for s in kwargs [ ' select ' ] ] )
# ORDER BY clause
order_by = [ ]
2005-07-22 21:02:27 +08:00
for f in handle_legacy_orderlist ( kwargs . get ( ' order_by ' , opts . ordering ) ) :
if f == ' ? ' : # Special case.
2005-09-03 04:46:00 +08:00
order_by . append ( db . get_random_function_sql ( ) )
2005-07-13 09:25:57 +08:00
else :
2005-10-06 23:52:30 +08:00
if f . startswith ( ' - ' ) :
col_name = f [ 1 : ]
order = " DESC "
else :
col_name = f
order = " ASC "
2005-07-22 21:02:27 +08:00
# Use the database table as a column prefix if it wasn't given,
2005-07-13 09:25:57 +08:00
# and if the requested column isn't a custom SELECT.
2005-10-06 23:52:30 +08:00
if " . " not in col_name and col_name not in [ k [ 0 ] for k in kwargs . get ( ' select ' , [ ] ) ] :
2005-07-22 21:02:27 +08:00
table_prefix = opts . db_table + ' . '
else :
table_prefix = ' '
2005-10-06 23:52:30 +08:00
order_by . append ( ' %s %s %s ' % ( table_prefix , orderfield2column ( col_name , opts ) , order ) )
2005-07-13 09:25:57 +08:00
order_by = " , " . join ( order_by )
# LIMIT and OFFSET clauses
if kwargs . get ( ' limit ' ) is not None :
2005-08-20 05:51:14 +08:00
limit_sql = " %s " % db . get_limit_offset_sql ( kwargs [ ' limit ' ] , kwargs . get ( ' offset ' ) )
2005-07-13 09:25:57 +08:00
else :
2005-08-20 05:51:14 +08:00
assert kwargs . get ( ' offset ' ) is None , " ' offset ' is not allowed without ' limit ' "
2005-07-13 09:25:57 +08:00
limit_sql = " "
return select , " FROM " + " , " . join ( tables ) + ( where and " WHERE " + " AND " . join ( where ) or " " ) + ( order_by and " ORDER BY " + order_by or " " ) + limit_sql , params
def function_get_in_bulk ( opts , klass , * args , * * kwargs ) :
id_list = args and args [ 0 ] or kwargs [ ' id_list ' ]
assert id_list != [ ] , " get_in_bulk() cannot be passed an empty list. "
kwargs [ ' where ' ] = [ " %s .id IN ( %s ) " % ( opts . db_table , " , " . join ( map ( str , id_list ) ) ) ]
obj_list = function_get_list ( opts , klass , * * kwargs )
return dict ( [ ( o . id , o ) for o in obj_list ] )
def function_get_latest ( opts , klass , does_not_exist_exception , * * kwargs ) :
2005-07-22 21:02:27 +08:00
kwargs [ ' order_by ' ] = ( ' - ' + opts . get_latest_by , )
2005-07-13 09:25:57 +08:00
kwargs [ ' limit ' ] = 1
return function_get_object ( opts , klass , does_not_exist_exception , * * kwargs )
def function_get_date_list ( opts , field , * args , * * kwargs ) :
2005-07-18 04:16:06 +08:00
from django . core . db . typecasts import typecast_timestamp
2005-07-13 09:25:57 +08:00
kind = args and args [ 0 ] or kwargs [ ' kind ' ]
assert kind in ( " month " , " year " , " day " ) , " ' kind ' must be one of ' year ' , ' month ' or ' day ' . "
order = ' ASC '
if kwargs . has_key ( ' _order ' ) :
order = kwargs [ ' _order ' ]
del kwargs [ ' _order ' ]
assert order in ( ' ASC ' , ' DESC ' ) , " ' order ' must be either ' ASC ' or ' DESC ' "
kwargs [ ' order_by ' ] = [ ] # Clear this because it'll mess things up otherwise.
if field . null :
2005-08-26 06:51:30 +08:00
kwargs . setdefault ( ' where ' , [ ] ) . append ( ' %s . %s IS NOT NULL ' % ( opts . db_table , field . column ) )
2005-07-13 09:25:57 +08:00
select , sql , params = function_get_sql_clause ( opts , * * kwargs )
2005-08-26 06:51:30 +08:00
sql = ' SELECT %s %s GROUP BY 1 ORDER BY 1 ' % ( db . get_date_trunc_sql ( kind , ' %s . %s ' % ( opts . db_table , field . column ) ) , sql )
2005-07-13 09:25:57 +08:00
cursor = db . db . cursor ( )
2005-07-18 04:16:06 +08:00
cursor . execute ( sql , params )
# We have to manually run typecast_timestamp(str()) on the results, because
# MySQL doesn't automatically cast the result of date functions as datetime
# objects -- MySQL returns the values as strings, instead.
return [ typecast_timestamp ( str ( row [ 0 ] ) ) for row in cursor . fetchall ( ) ]
2005-07-13 09:25:57 +08:00
###################################
# HELPER FUNCTIONS (MANIPULATORS) #
###################################
def get_manipulator ( opts , klass , extra_methods , add = False , change = False ) :
" Returns the custom Manipulator (either add or change) for the given opts. "
assert ( add == False or change == False ) and add != change , " get_manipulator() can be passed add=True or change=True, but not both "
man = types . ClassType ( ' %s Manipulator %s ' % ( opts . object_name , add and ' Add ' or ' Change ' ) , ( formfields . Manipulator , ) , { } )
man . __module__ = MODEL_PREFIX + ' . ' + opts . module_name # Set this explicitly, as above.
man . __init__ = curry ( manipulator_init , opts , add , change )
man . save = curry ( manipulator_save , opts , klass , add , change )
for field_name_list in opts . unique_together :
setattr ( man , ' isUnique %s ' % ' _ ' . join ( field_name_list ) , curry ( manipulator_validator_unique_together , field_name_list , opts ) )
for f in opts . fields :
if f . unique_for_date :
setattr ( man , ' isUnique %s For %s ' % ( f . name , f . unique_for_date ) , curry ( manipulator_validator_unique_for_date , f , opts . get_field ( f . unique_for_date ) , opts , ' date ' ) )
if f . unique_for_month :
setattr ( man , ' isUnique %s For %s ' % ( f . name , f . unique_for_month ) , curry ( manipulator_validator_unique_for_date , f , opts . get_field ( f . unique_for_month ) , opts , ' month ' ) )
if f . unique_for_year :
setattr ( man , ' isUnique %s For %s ' % ( f . name , f . unique_for_year ) , curry ( manipulator_validator_unique_for_date , f , opts . get_field ( f . unique_for_year ) , opts , ' year ' ) )
for k , v in extra_methods . items ( ) :
setattr ( man , k , v )
return man
def manipulator_init ( opts , add , change , self , obj_key = None ) :
if change :
assert obj_key is not None , " ChangeManipulator.__init__() must be passed obj_key parameter. "
self . obj_key = obj_key
try :
2005-08-11 03:32:38 +08:00
self . original_object = opts . get_model_module ( ) . get_object ( pk = obj_key )
2005-07-13 09:25:57 +08:00
except ObjectDoesNotExist :
# If the object doesn't exist, this might be a manipulator for a
# one-to-one related object that hasn't created its subobject yet.
# For example, this might be a Restaurant for a Place that doesn't
# yet have restaurant information.
if opts . one_to_one_field :
# Sanity check -- Make sure the "parent" object exists.
# For example, make sure the Place exists for the Restaurant.
# Let the ObjectDoesNotExist exception propogate up.
lookup_kwargs = opts . one_to_one_field . rel . limit_choices_to
lookup_kwargs [ ' %s __exact ' % opts . one_to_one_field . rel . field_name ] = obj_key
_ = opts . one_to_one_field . rel . to . get_model_module ( ) . get_object ( * * lookup_kwargs )
2005-08-26 06:51:30 +08:00
params = dict ( [ ( f . column , f . get_default ( ) ) for f in opts . fields ] )
params [ opts . pk . column ] = obj_key
2005-07-13 09:25:57 +08:00
self . original_object = opts . get_model_module ( ) . Klass ( * * params )
else :
raise
self . fields = [ ]
for f in opts . fields + opts . many_to_many :
2005-08-11 03:32:38 +08:00
if f . editable and not ( f . primary_key and change ) and ( not f . rel or not f . rel . edit_inline ) :
2005-07-13 09:25:57 +08:00
self . fields . extend ( f . get_manipulator_fields ( opts , self , change ) )
# Add fields for related objects.
for rel_opts , rel_field in opts . get_inline_related_objects ( ) :
if change :
count = getattr ( self . original_object , ' get_ %s _count ' % opts . get_rel_object_method_name ( rel_opts , rel_field ) ) ( )
count + = rel_field . rel . num_extra_on_change
if rel_field . rel . min_num_in_admin :
count = max ( count , rel_field . rel . min_num_in_admin )
if rel_field . rel . max_num_in_admin :
count = min ( count , rel_field . rel . max_num_in_admin )
else :
count = rel_field . rel . num_in_admin
for f in rel_opts . fields + rel_opts . many_to_many :
if f . editable and f != rel_field and ( not f . primary_key or ( f . primary_key and change ) ) :
for i in range ( count ) :
self . fields . extend ( f . get_manipulator_fields ( rel_opts , self , change , name_prefix = ' %s . %d . ' % ( rel_opts . object_name . lower ( ) , i ) , rel = True ) )
# Add field for ordering.
if change and opts . get_ordered_objects ( ) :
self . fields . append ( formfields . CommaSeparatedIntegerField ( field_name = " order_ " ) )
def manipulator_save ( opts , klass , add , change , self , new_data ) :
from django . utils . datastructures import DotExpandedDict
params = { }
for f in opts . fields :
# Fields with auto_now_add are another special case; they should keep
# their original value in the change stage.
if change and getattr ( f , ' auto_now_add ' , False ) :
2005-08-26 06:51:30 +08:00
params [ f . column ] = getattr ( self . original_object , f . name )
2005-07-13 09:25:57 +08:00
else :
2005-08-26 06:51:30 +08:00
params [ f . column ] = f . get_manipulator_new_data ( new_data )
2005-07-13 09:25:57 +08:00
if change :
2005-08-30 00:49:18 +08:00
params [ opts . pk . column ] = self . obj_key
2005-07-13 09:25:57 +08:00
# First, save the basic object itself.
new_object = klass ( * * params )
new_object . save ( )
# Now that the object's been saved, save any uploaded files.
for f in opts . fields :
if isinstance ( f , FileField ) :
f . save_file ( new_data , new_object , change and self . original_object or None , change , rel = False )
# Calculate which primary fields have changed.
if change :
self . fields_added , self . fields_changed , self . fields_deleted = [ ] , [ ] , [ ]
for f in opts . fields :
2005-08-26 06:51:30 +08:00
if not f . primary_key and str ( getattr ( self . original_object , f . column ) ) != str ( getattr ( new_object , f . column ) ) :
2005-07-13 09:25:57 +08:00
self . fields_changed . append ( f . verbose_name )
# Save many-to-many objects. Example: Poll.set_sites()
for f in opts . many_to_many :
if not f . rel . edit_inline :
was_changed = getattr ( new_object , ' set_ %s ' % f . name ) ( new_data . getlist ( f . name ) )
if change and was_changed :
self . fields_changed . append ( f . verbose_name )
# Save many-to-one objects. Example: Add the Choice objects for a Poll.
for rel_opts , rel_field in opts . get_inline_related_objects ( ) :
# Create obj_list, which is a DotExpandedDict such as this:
# [('0', {'id': ['940'], 'choice': ['This is the first choice']}),
# ('1', {'id': ['941'], 'choice': ['This is the second choice']}),
# ('2', {'id': [''], 'choice': ['']})]
obj_list = DotExpandedDict ( new_data . data ) [ rel_opts . object_name . lower ( ) ] . items ( )
obj_list . sort ( lambda x , y : cmp ( int ( x [ 0 ] ) , int ( y [ 0 ] ) ) )
params = { }
# For each related item...
for _ , rel_new_data in obj_list :
# Keep track of which core=True fields were provided.
# If all core fields were given, the related object will be saved.
# If none of the core fields were given, the object will be deleted.
# If some, but not all, of the fields were given, the validator would
# have caught that.
all_cores_given , all_cores_blank = True , True
# Get a reference to the old object. We'll use it to compare the
# old to the new, to see which fields have changed.
if change :
old_rel_obj = None
if rel_new_data [ rel_opts . pk . name ] [ 0 ] :
try :
old_rel_obj = getattr ( self . original_object , ' get_ %s ' % opts . get_rel_object_method_name ( rel_opts , rel_field ) ) ( * * { ' %s __exact ' % rel_opts . pk . name : rel_new_data [ rel_opts . pk . name ] [ 0 ] } )
except ObjectDoesNotExist :
pass
for f in rel_opts . fields :
if f . core and not isinstance ( f , FileField ) and f . get_manipulator_new_data ( rel_new_data , rel = True ) in ( None , ' ' ) :
all_cores_given = False
elif f . core and not isinstance ( f , FileField ) and f . get_manipulator_new_data ( rel_new_data , rel = True ) not in ( None , ' ' ) :
all_cores_blank = False
# If this field isn't editable, give it the same value it had
# previously, according to the given ID. If the ID wasn't
# given, use a default value. FileFields are also a special
# case, because they'll be dealt with later.
if change and ( isinstance ( f , FileField ) or not f . editable ) :
if rel_new_data . get ( rel_opts . pk . name , False ) and rel_new_data [ rel_opts . pk . name ] [ 0 ] :
2005-08-26 06:51:30 +08:00
params [ f . column ] = getattr ( old_rel_obj , f . column )
2005-07-13 09:25:57 +08:00
else :
2005-08-26 06:51:30 +08:00
params [ f . column ] = f . get_default ( )
2005-07-13 09:25:57 +08:00
elif f == rel_field :
2005-08-26 06:51:30 +08:00
params [ f . column ] = getattr ( new_object , rel_field . rel . field_name )
2005-07-13 09:25:57 +08:00
elif add and isinstance ( f , AutoField ) :
2005-08-26 06:51:30 +08:00
params [ f . column ] = None
2005-07-13 09:25:57 +08:00
else :
2005-08-26 06:51:30 +08:00
params [ f . column ] = f . get_manipulator_new_data ( rel_new_data , rel = True )
2005-07-13 09:25:57 +08:00
# Related links are a special case, because we have to
2005-09-03 00:34:14 +08:00
# manually set the "content_type_id" and "object_id" fields.
2005-07-13 09:25:57 +08:00
if opts . has_related_links and rel_opts . module_name == ' relatedlinks ' :
contenttypes_mod = get_module ( ' core ' , ' contenttypes ' )
params [ ' content_type_id ' ] = contenttypes_mod . get_object ( package__label__exact = opts . app_label , python_module_name__exact = opts . module_name ) . id
params [ ' object_id ' ] = new_object . id
# Create the related item.
new_rel_obj = rel_opts . get_model_module ( ) . Klass ( * * params )
# If all the core fields were provided (non-empty), save the item.
if all_cores_given :
new_rel_obj . save ( )
# Save any uploaded files.
for f in rel_opts . fields :
if isinstance ( f , FileField ) and rel_new_data . get ( f . name , False ) :
f . save_file ( rel_new_data , new_rel_obj , change and old_rel_obj or None , change , rel = True )
# Calculate whether any fields have changed.
if change :
if not old_rel_obj : # This object didn't exist before.
self . fields_added . append ( ' %s " %r " ' % ( rel_opts . verbose_name , new_rel_obj ) )
else :
for f in rel_opts . fields :
2005-08-26 06:51:30 +08:00
if not f . primary_key and f != rel_field and str ( getattr ( old_rel_obj , f . column ) ) != str ( getattr ( new_rel_obj , f . column ) ) :
2005-07-13 09:25:57 +08:00
self . fields_changed . append ( ' %s for %s " %r " ' % ( f . verbose_name , rel_opts . verbose_name , new_rel_obj ) )
# Save many-to-many objects.
for f in rel_opts . many_to_many :
if not f . rel . edit_inline :
was_changed = getattr ( new_rel_obj , ' set_ %s ' % f . name ) ( rel_new_data [ f . name ] )
if change and was_changed :
self . fields_changed . append ( ' %s for %s " %s " ' % ( f . verbose_name , rel_opts . verbose_name , new_rel_obj ) )
# If, in the change stage, all of the core fields were blank and
# the primary key (ID) was provided, delete the item.
if change and all_cores_blank and rel_new_data . has_key ( rel_opts . pk . name ) and rel_new_data [ rel_opts . pk . name ] [ 0 ] :
new_rel_obj . delete ( )
self . fields_deleted . append ( ' %s " %r " ' % ( rel_opts . verbose_name , old_rel_obj ) )
# Save the order, if applicable.
if change and opts . get_ordered_objects ( ) :
order = new_data [ ' order_ ' ] and map ( int , new_data [ ' order_ ' ] . split ( ' , ' ) ) or [ ]
for rel_opts in opts . get_ordered_objects ( ) :
getattr ( new_object , ' set_ %s _order ' % rel_opts . object_name . lower ( ) ) ( order )
return new_object
def manipulator_validator_unique_together ( field_name_list , opts , self , field_data , all_data ) :
from django . utils . text import get_text_list
field_list = [ opts . get_field ( field_name ) for field_name in field_name_list ]
2005-08-26 06:51:30 +08:00
if isinstance ( field_list [ 0 ] . rel , ManyToOne ) :
kwargs = { ' %s __ %s __iexact ' % ( field_name_list [ 0 ] , field_list [ 0 ] . rel . field_name ) : field_data }
else :
kwargs = { ' %s __iexact ' % field_name_list [ 0 ] : field_data }
2005-07-13 09:25:57 +08:00
for f in field_list [ 1 : ] :
2005-08-26 06:51:30 +08:00
field_val = all_data . get ( f . column , None )
2005-07-13 09:25:57 +08:00
if field_val is None :
# This will be caught by another validator, assuming the field
# doesn't have blank=True.
return
2005-08-26 06:51:30 +08:00
if isinstance ( f . rel , ManyToOne ) :
kwargs [ ' %s __pk ' % f . name ] = field_val
else :
kwargs [ ' %s __iexact ' % f . name ] = field_val
2005-07-13 09:25:57 +08:00
mod = opts . get_model_module ( )
try :
old_obj = mod . get_object ( * * kwargs )
except ObjectDoesNotExist :
return
2005-08-26 06:51:30 +08:00
if hasattr ( self , ' original_object ' ) and getattr ( self . original_object , opts . pk . column ) == getattr ( old_obj , opts . pk . column ) :
2005-07-13 09:25:57 +08:00
pass
else :
raise validators . ValidationError , " %s with this %s already exists for the given %s . " % \
( capfirst ( opts . verbose_name ) , field_list [ 0 ] . verbose_name , get_text_list ( field_name_list [ 1 : ] , ' and ' ) )
def manipulator_validator_unique_for_date ( from_field , date_field , opts , lookup_type , self , field_data , all_data ) :
date_str = all_data . get ( date_field . get_manipulator_field_names ( ' ' ) [ 0 ] , None )
mod = opts . get_model_module ( )
date_val = formfields . DateField . html2python ( date_str )
if date_val is None :
return # Date was invalid. This will be caught by another validator.
2005-08-30 00:44:40 +08:00
lookup_kwargs = { ' %s __year ' % date_field . name : date_val . year }
if isinstance ( from_field . rel , ManyToOne ) :
lookup_kwargs [ ' %s __pk ' % from_field . name ] = field_data
else :
lookup_kwargs [ ' %s __iexact ' % from_field . name ] = field_data
2005-07-13 09:25:57 +08:00
if lookup_type in ( ' month ' , ' date ' ) :
lookup_kwargs [ ' %s __month ' % date_field . name ] = date_val . month
if lookup_type == ' date ' :
lookup_kwargs [ ' %s __day ' % date_field . name ] = date_val . day
try :
old_obj = mod . get_object ( * * lookup_kwargs )
except ObjectDoesNotExist :
return
else :
2005-08-26 06:51:30 +08:00
if hasattr ( self , ' original_object ' ) and getattr ( self . original_object , opts . pk . column ) == getattr ( old_obj , opts . pk . column ) :
2005-07-13 09:25:57 +08:00
pass
else :
format_string = ( lookup_type == ' date ' ) and ' % B %d , % Y ' or ' % B % Y '
raise validators . ValidationError , " Please enter a different %s . The one you entered is already being used for %s . " % \
( from_field . verbose_name , date_val . strftime ( format_string ) )