| 1 |
from django.conf import settings |
|---|
| 2 |
from django.core import formfields, validators |
|---|
| 3 |
from django.core import db |
|---|
| 4 |
from django.core.exceptions import ObjectDoesNotExist |
|---|
| 5 |
from django.core.meta.fields import * |
|---|
| 6 |
from django.utils.functional import curry |
|---|
| 7 |
from django.utils.text import capfirst |
|---|
| 8 |
import copy, datetime, os, re, sys, types |
|---|
| 9 |
|
|---|
| 10 |
# Admin stages. |
|---|
| 11 |
ADD, CHANGE, BOTH = 1, 2, 3 |
|---|
| 12 |
|
|---|
| 13 |
# Size of each "chunk" for get_iterator calls. |
|---|
| 14 |
# Larger values are slightly faster at the expense of more storage space. |
|---|
| 15 |
GET_ITERATOR_CHUNK_SIZE = 100 |
|---|
| 16 |
|
|---|
| 17 |
# Prefix (in Python path style) to location of models. |
|---|
| 18 |
MODEL_PREFIX = 'django.models' |
|---|
| 19 |
|
|---|
| 20 |
# Methods on models with the following prefix will be removed and |
|---|
| 21 |
# converted to module-level functions. |
|---|
| 22 |
MODEL_FUNCTIONS_PREFIX = '_module_' |
|---|
| 23 |
|
|---|
| 24 |
# Methods on models with the following prefix will be removed and |
|---|
| 25 |
# converted to manipulator methods. |
|---|
| 26 |
MANIPULATOR_FUNCTIONS_PREFIX = '_manipulator_' |
|---|
| 27 |
|
|---|
| 28 |
LOOKUP_SEPARATOR = '__' |
|---|
| 29 |
|
|---|
| 30 |
#################### |
|---|
| 31 |
# HELPER FUNCTIONS # |
|---|
| 32 |
#################### |
|---|
| 33 |
|
|---|
| 34 |
# Django currently supports two forms of ordering. |
|---|
| 35 |
# Form 1 (deprecated) example: |
|---|
| 36 |
# order_by=(('pub_date', 'DESC'), ('headline', 'ASC'), (None, 'RANDOM')) |
|---|
| 37 |
# Form 2 (new-style) example: |
|---|
| 38 |
# order_by=('-pub_date', 'headline', '?') |
|---|
| 39 |
# Form 1 is deprecated and will no longer be supported for Django's first |
|---|
| 40 |
# official release. The following code converts from Form 1 to Form 2. |
|---|
| 41 |
|
|---|
| 42 |
LEGACY_ORDERING_MAPPING = {'ASC': '_', 'DESC': '-_', 'RANDOM': '?'} |
|---|
| 43 |
|
|---|
| 44 |
def handle_legacy_orderlist(order_list): |
|---|
| 45 |
if not order_list or isinstance(order_list[0], basestring): |
|---|
| 46 |
return order_list |
|---|
| 47 |
else: |
|---|
| 48 |
import warnings |
|---|
| 49 |
new_order_list = [LEGACY_ORDERING_MAPPING[j.upper()].replace('_', str(i)) for i, j in order_list] |
|---|
| 50 |
warnings.warn("%r ordering syntax is deprecated. Use %r instead." % (order_list, new_order_list), DeprecationWarning) |
|---|
| 51 |
return new_order_list |
|---|
| 52 |
|
|---|
| 53 |
def orderlist2sql(order_list, prefix=''): |
|---|
| 54 |
output = [] |
|---|
| 55 |
for f in handle_legacy_orderlist(order_list): |
|---|
| 56 |
if f.startswith('-'): |
|---|
| 57 |
output.append('%s%s DESC' % (prefix, f[1:])) |
|---|
| 58 |
elif f == '?': |
|---|
| 59 |
output.append('RANDOM()') |
|---|
| 60 |
else: |
|---|
| 61 |
output.append('%s%s ASC' % (prefix, f)) |
|---|
| 62 |
return ', '.join(output) |
|---|
| 63 |
|
|---|
| 64 |
def get_module(app_label, module_name): |
|---|
| 65 |
return __import__('%s.%s.%s' % (MODEL_PREFIX, app_label, module_name), '', '', ['']) |
|---|
| 66 |
|
|---|
| 67 |
def get_app(app_label): |
|---|
| 68 |
return __import__('%s.%s' % (MODEL_PREFIX, app_label), '', '', ['']) |
|---|
| 69 |
|
|---|
| 70 |
_installed_models_cache = None |
|---|
| 71 |
def get_installed_models(): |
|---|
| 72 |
""" |
|---|
| 73 |
Returns a list of installed "models" packages, such as foo.models, |
|---|
| 74 |
ellington.news.models, etc. This does NOT include django.models. |
|---|
| 75 |
""" |
|---|
| 76 |
global _installed_models_cache |
|---|
| 77 |
if _installed_models_cache is not None: |
|---|
| 78 |
return _installed_models_cache |
|---|
| 79 |
_installed_models_cache = [] |
|---|
| 80 |
for a in settings.INSTALLED_APPS: |
|---|
| 81 |
try: |
|---|
| 82 |
_installed_models_cache.append(__import__(a + '.models', '', '', [''])) |
|---|
| 83 |
except ImportError: |
|---|
| 84 |
pass |
|---|
| 85 |
return _installed_models_cache |
|---|
| 86 |
|
|---|
| 87 |
_installed_modules_cache = None |
|---|
| 88 |
def get_installed_model_modules(core_models=None): |
|---|
| 89 |
""" |
|---|
| 90 |
Returns a list of installed models, such as django.models.core, |
|---|
| 91 |
ellington.news.models.news, foo.models.bar, etc. |
|---|
| 92 |
""" |
|---|
| 93 |
global _installed_modules_cache |
|---|
| 94 |
if _installed_modules_cache is not None: |
|---|
| 95 |
return _installed_modules_cache |
|---|
| 96 |
_installed_modules_cache = [] |
|---|
| 97 |
|
|---|
| 98 |
# django.models is a special case. |
|---|
| 99 |
for submodule in (core_models or []): |
|---|
| 100 |
_installed_modules_cache.append(__import__('django.models.%s' % submodule, '', '', [''])) |
|---|
| 101 |
for m in get_installed_models(): |
|---|
| 102 |
for submodule in getattr(m, '__all__', []): |
|---|
| 103 |
mod = __import__('django.models.%s' % submodule, '', '', ['']) |
|---|
| 104 |
try: |
|---|
| 105 |
mod._MODELS |
|---|
| 106 |
except AttributeError: |
|---|
| 107 |
pass # Skip model modules that don't actually have models in them. |
|---|
| 108 |
else: |
|---|
| 109 |
_installed_modules_cache.append(mod) |
|---|
| 110 |
return _installed_modules_cache |
|---|
| 111 |
|
|---|
| 112 |
class LazyDate: |
|---|
| 113 |
""" |
|---|
| 114 |
Use in limit_choices_to to compare the field to dates calculated at run time |
|---|
| 115 |
instead of when the model is loaded. For example:: |
|---|
| 116 |
|
|---|
| 117 |
... limit_choices_to = {'date__gt' : meta.LazyDate(days=-3)} ... |
|---|
| 118 |
|
|---|
| 119 |
which will limit the choices to dates greater than three days ago. |
|---|
| 120 |
""" |
|---|
| 121 |
def __init__(self, **kwargs): |
|---|
| 122 |
self.delta = datetime.timedelta(**kwargs) |
|---|
| 123 |
|
|---|
| 124 |
def __str__(self): |
|---|
| 125 |
return str(self.__get_value__()) |
|---|
| 126 |
|
|---|
| 127 |
def __repr__(self): |
|---|
| 128 |
return "<LazyDate: %s>" % self.delta |
|---|
| 129 |
|
|---|
| 130 |
def __get_value__(self): |
|---|
| 131 |
return datetime.datetime.now() + self.delta |
|---|
| 132 |
|
|---|
| 133 |
################ |
|---|
| 134 |
# MAIN CLASSES # |
|---|
| 135 |
################ |
|---|
| 136 |
|
|---|
| 137 |
class FieldDoesNotExist(Exception): |
|---|
| 138 |
pass |
|---|
| 139 |
|
|---|
| 140 |
class BadKeywordArguments(Exception): |
|---|
| 141 |
pass |
|---|
| 142 |
|
|---|
| 143 |
class Options: |
|---|
| 144 |
def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', |
|---|
| 145 |
fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, |
|---|
| 146 |
where_constraints=None, object_name=None, app_label=None, |
|---|
| 147 |
exceptions=None, permissions=None, get_latest_by=None, |
|---|
| 148 |
order_with_respect_to=None, module_constants=None): |
|---|
| 149 |
|
|---|
| 150 |
# Save the original function args, for use by copy(). Note that we're |
|---|
| 151 |
# NOT using copy.deepcopy(), because that would create a new copy of |
|---|
| 152 |
# everything in memory, and it's better to conserve memory. Of course, |
|---|
| 153 |
# this comes with the important gotcha that changing any attribute of |
|---|
| 154 |
# this object will change its value in self._orig_init_args, so we |
|---|
| 155 |
# need to be careful not to do that. In practice, we can pull this off |
|---|
| 156 |
# because Options are generally read-only objects, and __init__() is |
|---|
| 157 |
# the only place where its attributes are manipulated. |
|---|
| 158 |
|
|---|
| 159 |
# locals() is used purely for convenience, so we don't have to do |
|---|
| 160 |
# something verbose like this: |
|---|
| 161 |
# self._orig_init_args = { |
|---|
| 162 |
# 'module_name': module_name, |
|---|
| 163 |
# 'verbose_name': verbose_name, |
|---|
| 164 |
# ... |
|---|
| 165 |
# } |
|---|
| 166 |
self._orig_init_args = locals() |
|---|
| 167 |
del self._orig_init_args['self'] # because we don't care about it. |
|---|
| 168 |
|
|---|
| 169 |
# Move many-to-many related fields from self.fields into self.many_to_many. |
|---|
| 170 |
self.fields, self.many_to_many = [], [] |
|---|
| 171 |
for field in (fields or []): |
|---|
| 172 |
if field.rel and isinstance(field.rel, ManyToMany): |
|---|
| 173 |
self.many_to_many.append(field) |
|---|
| 174 |
else: |
|---|
| 175 |
self.fields.append(field) |
|---|
| 176 |
self.module_name, self.verbose_name = module_name, verbose_name |
|---|
| 177 |
self.verbose_name_plural = verbose_name_plural or verbose_name + 's' |
|---|
| 178 |
self.db_table, self.has_related_links = db_table, has_related_links |
|---|
| 179 |
self.ordering = ordering or [] |
|---|
| 180 |
self.unique_together = unique_together or [] |
|---|
| 181 |
self.where_constraints = where_constraints or [] |
|---|
| 182 |
self.exceptions = exceptions or [] |
|---|
| 183 |
self.permissions = permissions or [] |
|---|
| 184 |
self.object_name, self.app_label = object_name, app_label |
|---|
| 185 |
self.get_latest_by = get_latest_by |
|---|
| 186 |
if order_with_respect_to: |
|---|
| 187 |
self.order_with_respect_to = self.get_field(order_with_respect_to) |
|---|
| 188 |
self.ordering = ('_order',) |
|---|
| 189 |
else: |
|---|
| 190 |
self.order_with_respect_to = None |
|---|
| 191 |
self.module_constants = module_constants or {} |
|---|
| 192 |
self.admin = admin |
|---|
| 193 |
|
|---|
| 194 |
# Calculate one_to_one_field. |
|---|
| 195 |
self.one_to_one_field = None |
|---|
| 196 |
for f in self.fields: |
|---|
| 197 |
if isinstance(f.rel, OneToOne): |
|---|
| 198 |
self.one_to_one_field = f |
|---|
| 199 |
break |
|---|
| 200 |
# Cache the primary-key field. |
|---|
| 201 |
self.pk = None |
|---|
| 202 |
for f in self.fields: |
|---|
| 203 |
if f.primary_key: |
|---|
| 204 |
self.pk = f |
|---|
| 205 |
break |
|---|
| 206 |
# If a primary_key field hasn't been specified, add an |
|---|
| 207 |
# auto-incrementing primary-key ID field automatically. |
|---|
| 208 |
if self.pk is None: |
|---|
| 209 |
self.fields.insert(0, AutoField('id', 'ID', primary_key=True)) |
|---|
| 210 |
self.pk = self.fields[0] |
|---|
| 211 |
# Cache whether this has an AutoField. |
|---|
| 212 |
self.has_auto_field = False |
|---|
| 213 |
for f in self.fields: |
|---|
| 214 |
is_auto = isinstance(f, AutoField) |
|---|
| 215 |
if is_auto and self.has_auto_field: |
|---|
| 216 |
raise AssertionError, "A model can't have more than one AutoField." |
|---|
| 217 |
elif is_auto: |
|---|
| 218 |
self.has_auto_field = True |
|---|
| 219 |
|
|---|
| 220 |
def __repr__(self): |
|---|
| 221 |
return '<Options for %s>' % self.module_name |
|---|
| 222 |
|
|---|
| 223 |
def copy(self, **kwargs): |
|---|
| 224 |
args = self._orig_init_args.copy() |
|---|
| 225 |
args.update(kwargs) |
|---|
| 226 |
return self.__class__(**args) |
|---|
| 227 |
|
|---|
| 228 |
def get_model_module(self): |
|---|
| 229 |
return get_module(self.app_label, self.module_name) |
|---|
| 230 |
|
|---|
| 231 |
def get_content_type_id(self): |
|---|
| 232 |
"Returns the content-type ID for this object type." |
|---|
| 233 |
if not hasattr(self, '_content_type_id'): |
|---|
| 234 |
mod = get_module('core', 'contenttypes') |
|---|
| 235 |
self._content_type_id = mod.get_object(python_module_name__exact=self.module_name, package__label__exact=self.app_label).id |
|---|
| 236 |
return self._content_type_id |
|---|
| 237 |
|
|---|
| 238 |
def get_field(self, name, many_to_many=True): |
|---|
| 239 |
""" |
|---|
| 240 |
Returns the requested field by name. Raises FieldDoesNotExist on error. |
|---|
| 241 |
""" |
|---|
| 242 |
to_search = many_to_many and (self.fields + self.many_to_many) or self.fields |
|---|
| 243 |
for f in to_search: |
|---|
| 244 |
if f.name == name: |
|---|
| 245 |
return f |
|---|
| 246 |
raise FieldDoesNotExist, "name=%s" % name |
|---|
| 247 |
|
|---|
| 248 |
def get_order_sql(self, table_prefix=''): |
|---|
| 249 |
"Returns the full 'ORDER BY' clause for this object, according to self.ordering." |
|---|
| 250 |
if not self.ordering: return '' |
|---|
| 251 |
pre = table_prefix and (table_prefix + '.') or '' |
|---|
| 252 |
return 'ORDER BY ' + orderlist2sql(self.ordering, pre) |
|---|
| 253 |
|
|---|
| 254 |
def get_add_permission(self): |
|---|
| 255 |
return 'add_%s' % self.object_name.lower() |
|---|
| 256 |
|
|---|
| 257 |
def get_change_permission(self): |
|---|
| 258 |
return 'change_%s' % self.object_name.lower() |
|---|
| 259 |
|
|---|
| 260 |
def get_delete_permission(self): |
|---|
| 261 |
return 'delete_%s' % self.object_name.lower() |
|---|
| 262 |
|
|---|
| 263 |
def get_rel_object_method_name(self, rel_opts, rel_field): |
|---|
| 264 |
# This method encapsulates the logic that decides what name to give a |
|---|
| 265 |
# method that retrieves related many-to-one objects. Usually it just |
|---|
| 266 |
# uses the lower-cased object_name, but if the related object is in |
|---|
| 267 |
# another app, its app_label is appended. |
|---|
| 268 |
# |
|---|
| 269 |
# Examples: |
|---|
| 270 |
# |
|---|
| 271 |
# # Normal case -- a related object in the same app. |
|---|
| 272 |
# # This method returns "choice". |
|---|
| 273 |
# Poll.get_choice_list() |
|---|
| 274 |
# |
|---|
| 275 |
# # A related object in a different app. |
|---|
| 276 |
# # This method returns "lcom_bestofaward". |
|---|
| 277 |
# Place.get_lcom_bestofaward_list() # "lcom_bestofaward" |
|---|
| 278 |
rel_obj_name = rel_field.rel.related_name or rel_opts.object_name.lower() |
|---|
| 279 |
if self.app_label != rel_opts.app_label: |
|---|
| 280 |
rel_obj_name = '%s_%s' % (rel_opts.app_label, rel_obj_name) |
|---|
| 281 |
return rel_obj_name |
|---|
| 282 |
|
|---|
| 283 |
def get_all_related_objects(self): |
|---|
| 284 |
try: # Try the cache first. |
|---|
| 285 |
return self._all_related_objects |
|---|
| 286 |
except AttributeError: |
|---|
| 287 |
module_list = get_installed_model_modules() |
|---|
| 288 |
rel_objs = [] |
|---|
| 289 |
for mod in module_list: |
|---|
| 290 |
for klass in mod._MODELS: |
|---|
| 291 |
for f in klass._meta.fields: |
|---|
| 292 |
if f.rel and self == f.rel.to: |
|---|
| 293 |
rel_objs.append((klass._meta, f)) |
|---|
| 294 |
if self.has_related_links: |
|---|
| 295 |
# Manually add RelatedLink objects, which are a special case. |
|---|
| 296 |
core = get_module('relatedlinks', 'relatedlinks') |
|---|
| 297 |
# Note that the copy() is very important -- otherwise any |
|---|
| 298 |
# subsequently loaded object with related links will override this |
|---|
| 299 |
# relationship we're adding. |
|---|
| 300 |
link_field = copy.copy(core.RelatedLink._meta.get_field('object_id')) |
|---|
| 301 |
link_field.rel = ManyToOne(self.get_model_module().Klass, 'related_links', 'id', |
|---|
| 302 |
num_in_admin=3, min_num_in_admin=3, edit_inline=TABULAR, |
|---|
| 303 |
lookup_overrides={ |
|---|
| 304 |
'content_type__package__label__exact': self.app_label, |
|---|
| 305 |
'content_type__python_module_name__exact': self.module_name |
|---|
| 306 |
}) |
|---|
| 307 |
rel_objs.append((core.RelatedLink._meta, link_field)) |
|---|
| 308 |
self._all_related_objects = rel_objs |
|---|
| 309 |
return rel_objs |
|---|
| 310 |
|
|---|
| 311 |
def get_inline_related_objects(self): |
|---|
| 312 |
return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] |
|---|
| 313 |
|
|---|
| 314 |
def get_all_related_many_to_many_objects(self): |
|---|
| 315 |
module_list = get_installed_model_modules() |
|---|
| 316 |
rel_objs = [] |
|---|
| 317 |
for mod in module_list: |
|---|
| 318 |
for klass in mod._MODELS: |
|---|
| 319 |
try: |
|---|
| 320 |
for f in klass._meta.many_to_many: |
|---|
| 321 |
if f.rel and self == f.rel.to: |
|---|
| 322 |
rel_objs.append((klass._meta, f)) |
|---|
| 323 |
raise StopIteration |
|---|
| 324 |
except StopIteration: |
|---|
| 325 |
continue |
|---|
| 326 |
return rel_objs |
|---|
| 327 |
|
|---|
| 328 |
def get_ordered_objects(self): |
|---|
| 329 |
"Returns a list of Options objects that are ordered with respect to this object." |
|---|
| 330 |
if not hasattr(self, '_ordered_objects'): |
|---|
| 331 |
objects = [] |
|---|
| 332 |
for klass in get_app(self.app_label)._MODELS: |
|---|
| 333 |
opts = klass._meta |
|---|
| 334 |
if opts.order_with_respect_to and opts.order_with_respect_to.rel \ |
|---|
| 335 |
and self == opts.order_with_respect_to.rel.to: |
|---|
| 336 |
objects.append(opts) |
|---|
| 337 |
self._ordered_objects = objects |
|---|
| 338 |
return self._ordered_objects |
|---|
| 339 |
|
|---|
| 340 |
def has_field_type(self, field_type): |
|---|
| 341 |
""" |
|---|
| 342 |
Returns True if this object's admin form has at least one of the given |
|---|
| 343 |
field_type (e.g. FileField). |
|---|
| 344 |
""" |
|---|
| 345 |
if not hasattr(self, '_field_types'): |
|---|
| 346 |
self._field_types = {} |
|---|
| 347 |
if not self._field_types.has_key(field_type): |
|---|
| 348 |
try: |
|---|
| 349 |
# First check self.fields. |
|---|
| 350 |
for f in self.fields: |
|---|
| 351 |
if isinstance(f, field_type): |
|---|
| 352 |
raise StopIteration |
|---|
| 353 |
# Failing that, check related fields. |
|---|
| 354 |
for rel_obj, rel_field in self.get_inline_related_objects(): |
|---|
| 355 |
for f in rel_obj.fields: |
|---|
| 356 |
if isinstance(f, field_type): |
|---|
| 357 |
raise StopIteration |
|---|
| 358 |
except StopIteration: |
|---|
| 359 |
self._field_types[field_type] = True |
|---|
| 360 |
else: |
|---|
| 361 |
self._field_types[field_type] = False |
|---|
| 362 |
return self._field_types[field_type] |
|---|
| 363 |
|
|---|
| 364 |
def _reassign_globals(function_dict, extra_globals, namespace): |
|---|
| 365 |
new_functions = {} |
|---|
| 366 |
for k, v in function_dict.items(): |
|---|
| 367 |
# Get the code object. |
|---|
| 368 |
code = v.func_code |
|---|
| 369 |
# Recreate the function, but give it access to extra_globals and the |
|---|
| 370 |
# given namespace's globals, too. |
|---|
| 371 |
new_globals = {'__builtins__': __builtins__, 'db': db.db, 'datetime': datetime} |
|---|
| 372 |
new_globals.update(extra_globals.__dict__) |
|---|
| 373 |
func = types.FunctionType(code, globals=new_globals, name=k, argdefs=v.func_defaults) |
|---|
| 374 |
func.__dict__.update(v.__dict__) |
|---|
| 375 |
setattr(namespace, k, func) |
|---|
| 376 |
# For all of the custom functions that have been added so far, give |
|---|
| 377 |
# them access to the new function we've just created. |
|---|
| 378 |
for new_k, new_v in new_functions.items(): |
|---|
| 379 |
new_v.func_globals[k] = func |
|---|
| 380 |
new_functions[k] = func |
|---|
| 381 |
|
|---|
| 382 |
class ModelBase(type): |
|---|
| 383 |
"Metaclass for all models" |
|---|
| 384 |
def __new__(cls, name, bases, attrs): |
|---|
| 385 |
# If this isn't a subclass of Model, don't do anything special. |
|---|
| 386 |
if not bases: |
|---|
| 387 |
return type.__new__(cls, name, bases, attrs) |
|---|
| 388 |
|
|---|
| 389 |
# If this model is a subclass of another Model, create an Options |
|---|
| 390 |
# object by first copying the base class's _meta and then updating it |
|---|
| 391 |
# with the overrides from this class. |
|---|
| 392 |
replaces_module = None |
|---|
| 393 |
if bases[0] != Model: |
|---|
| 394 |
if not attrs.has_key('fields'): |
|---|
| 395 |
attrs['fields'] = list(bases[0]._meta._orig_init_args['fields'][:]) |
|---|
| 396 |
if attrs.has_key('ignore_fields'): |
|---|
| 397 |
ignore_fields = attrs.pop('ignore_fields') |
|---|
| 398 |
new_fields = [] |
|---|
| 399 |
for i, f in enumerate(attrs['fields']): |
|---|
| 400 |
if f.name not in ignore_fields: |
|---|
| 401 |
new_fields.append(f) |
|---|
| 402 |
attrs['fields'] = new_fields |
|---|
| 403 |
if attrs.has_key('add_fields'): |
|---|
| 404 |
attrs['fields'].extend(attrs.pop('add_fields')) |
|---|
| 405 |
if attrs.has_key('replaces_module'): |
|---|
| 406 |
# Set the replaces_module variable for now. We can't actually |
|---|
| 407 |
# do anything with it yet, because the module hasn't yet been |
|---|
| 408 |
# created. |
|---|
| 409 |
replaces_module = attrs.pop('replaces_module').split('.') |
|---|
| 410 |
# Pass any Options overrides to the base's Options instance, and |
|---|
| 411 |
# simultaneously remove them from attrs. When this is done, attrs |
|---|
| 412 |
# will be a dictionary of custom methods, plus __module__. |
|---|
| 413 |
meta_overrides = {} |
|---|
| 414 |
for k, v in attrs.items(): |
|---|
| 415 |
if not callable(v) and k != '__module__': |
|---|
| 416 |
meta_overrides[k] = attrs.pop(k) |
|---|
| 417 |
opts = bases[0]._meta.copy(**meta_overrides) |
|---|
| 418 |
opts.object_name = name |
|---|
| 419 |
del meta_overrides |
|---|
| 420 |
else: |
|---|
| 421 |
opts = Options( |
|---|
| 422 |
# If the module_name wasn't given, use the class name |
|---|
| 423 |
# in lowercase, plus a trailing "s" -- a poor-man's |
|---|
| 424 |
# pluralization. |
|---|
| 425 |
module_name = attrs.pop('module_name', name.lower() + 's'), |
|---|
| 426 |
# If the verbose_name wasn't given, use the class name, |
|---|
| 427 |
# converted from InitialCaps to "lowercase with spaces". |
|---|
| 428 |
verbose_name = attrs.pop('verbose_name', |
|---|
| 429 |
re.sub('([A-Z])', ' \\1', name).lower().strip()), |
|---|
| 430 |
verbose_name_plural = attrs.pop('verbose_name_plural', ''), |
|---|
| 431 |
db_table = attrs.pop('db_table', ''), |
|---|
| 432 |
fields = attrs.pop('fields'), |
|---|
| 433 |
ordering = attrs.pop('ordering', None), |
|---|
| 434 |
unique_together = attrs.pop('unique_together', None), |
|---|
| 435 |
admin = attrs.pop('admin', None), |
|---|
| 436 |
has_related_links = attrs.pop('has_related_links', False), |
|---|
| 437 |
where_constraints = attrs.pop('where_constraints', None), |
|---|
| 438 |
object_name = name, |
|---|
| 439 |
app_label = attrs.pop('app_label', None), |
|---|
| 440 |
exceptions = attrs.pop('exceptions', None), |
|---|
| 441 |
permissions = attrs.pop('permissions', None), |
|---|
| 442 |
get_latest_by = attrs.pop('get_latest_by', None), |
|---|
| 443 |
order_with_respect_to = attrs.pop('order_with_respect_to', None), |
|---|
| 444 |
module_constants = attrs.pop('module_constants', None), |
|---|
| 445 |
) |
|---|
| 446 |
|
|---|
| 447 |
# Dynamically create the module that will contain this class and its |
|---|
| 448 |
# associated helper functions. |
|---|
| 449 |
if replaces_module is not None: |
|---|
| 450 |
new_mod = get_module(*replaces_module) |
|---|
| 451 |
else: |
|---|
| 452 |
new_mod = types.ModuleType(opts.module_name) |
|---|
| 453 |
|
|---|
| 454 |
# Collect any/all custom class methods and module functions, and move |
|---|
| 455 |
# them to a temporary holding variable. We'll deal with them later. |
|---|
| 456 |
if replaces_module is not None: |
|---|
| 457 |
# Initialize these values to the base class' custom_methods and |
|---|
| 458 |
# custom_functions. |
|---|
| 459 |
custom_methods = dict([(k, v) for k, v in new_mod.Klass.__dict__.items() if hasattr(v, 'custom')]) |
|---|
| 460 |
custom_functions = dict([(k, v) for k, v in new_mod.__dict__.items() if hasattr(v, 'custom')]) |
|---|
| 461 |
else: |
|---|
| 462 |
custom_methods, custom_functions = {}, {} |
|---|
| 463 |
manipulator_methods = {} |
|---|
| 464 |
for k, v in attrs.items(): |
|---|
| 465 |
if k in ('__module__', '__init__', '_overrides', '__doc__'): |
|---|
| 466 |
continue # Skip the important stuff. |
|---|
| 467 |
# Give the function a function attribute "custom" to designate that |
|---|
| 468 |
# it's a custom function/method. |
|---|
| 469 |
v.custom = True |
|---|
| 470 |
if k.startswith(MODEL_FUNCTIONS_PREFIX): |
|---|
| 471 |
custom_functions[k[len(MODEL_FUNCTIONS_PREFIX):]] = v |
|---|
| 472 |
elif k.startswith(MANIPULATOR_FUNCTIONS_PREFIX): |
|---|
| 473 |
manipulator_methods[k[len(MANIPULATOR_FUNCTIONS_PREFIX):]] = v |
|---|
| 474 |
else: |
|---|
| 475 |
custom_methods[k] = v |
|---|
| 476 |
del attrs[k] |
|---|
| 477 |
|
|---|
| 478 |
# Create the module-level ObjectDoesNotExist exception. |
|---|
| 479 |
dne_exc_name = '%sDoesNotExist' % name |
|---|
| 480 |
does_not_exist_exception = types.ClassType(dne_exc_name, (ObjectDoesNotExist,), {}) |
|---|
| 481 |
# Explicitly set its __module__ because it will initially (incorrectly) |
|---|
| 482 |
# be set to the module the code is being executed in. |
|---|
| 483 |
does_not_exist_exception.__module__ = MODEL_PREFIX + '.' + opts.module_name |
|---|
| 484 |
setattr(new_mod, dne_exc_name, does_not_exist_exception) |
|---|
| 485 |
|
|---|
| 486 |
# Create other exceptions. |
|---|
| 487 |
for exception_name in opts.exceptions: |
|---|
| 488 |
exc = types.ClassType(exception_name, (Exception,), {}) |
|---|
| 489 |
exc.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above. |
|---|
| 490 |
setattr(new_mod, exception_name, exc) |
|---|
| 491 |
|
|---|
| 492 |
# Create any module-level constants, if applicable. |
|---|
| 493 |
for k, v in opts.module_constants.items(): |
|---|
| 494 |
setattr(new_mod, k, v) |
|---|
| 495 |
|
|---|
| 496 |
# Create the default class methods. |
|---|
| 497 |
attrs['__init__'] = curry(method_init, opts) |
|---|
| 498 |
attrs['__eq__'] = curry(method_eq, opts |
|---|