1 import django
.db
.models
.manipulators
2 import django
.db
.models
.manager
3 from django
.core
import validators
4 from django
.core
.exceptions
import ObjectDoesNotExist
, MultipleObjectsReturned
5 from django
.db
.models
.fields
import AutoField
, ImageField
, FieldDoesNotExist
6 from django
.db
.models
.fields
.related
import OneToOneRel
, ManyToOneRel
7 from django
.db
.models
.query
import delete_objects
8 from django
.db
.models
.options
import Options
, AdminOptions
9 from django
.db
import connection
, transaction
10 from django
.db
.models
import signals
11 from django
.db
.models
.loading
import register_models
, get_model
12 from django
.dispatch
import dispatcher
13 from django
.utils
.datastructures
import SortedDict
14 from django
.utils
.functional
import curry
15 from django
.utils
.encoding
import smart_str
, force_unicode
, smart_unicode
16 from django
.conf
import settings
17 from itertools
import izip
22 class ModelBase(type):
23 "Metaclass for all models"
24 def __new__(cls
, name
, bases
, attrs
):
25 # If this isn't a subclass of Model, don't do anything special.
27 parents
= [b
for b
in bases
if issubclass(b
, Model
)]
29 return super(ModelBase
, cls
).__new
__(cls
, name
, bases
, attrs
)
31 # 'Model' isn't defined yet, meaning we're looking at Django's own
32 # Model class, defined below.
33 return super(ModelBase
, cls
).__new
__(cls
, name
, bases
, attrs
)
36 new_class
= type.__new
__(cls
, name
, bases
, {'__module__': attrs
.pop('__module__')})
37 new_class
.add_to_class('_meta', Options(attrs
.pop('Meta', None)))
38 new_class
.add_to_class('DoesNotExist', types
.ClassType('DoesNotExist', (ObjectDoesNotExist
,), {}))
39 new_class
.add_to_class('MultipleObjectsReturned',
40 types
.ClassType('MultipleObjectsReturned', (MultipleObjectsReturned
, ), {}))
42 # Build complete list of parents
45 new_class
._meta
.parents
.append(base
)
46 new_class
._meta
.parents
.extend(base
._meta
.parents
)
49 if getattr(new_class
._meta
, 'app_label', None) is None:
50 # Figure out the app_label by looking one level up.
51 # For 'django.contrib.sites.models', this would be 'sites'.
52 model_module
= sys
.modules
[new_class
.__module
__]
53 new_class
._meta
.app_label
= model_module
.__name
__.split('.')[-2]
55 # Bail out early if we have already created this class.
56 m
= get_model(new_class
._meta
.app_label
, name
, False)
60 # Add all attributes to the class.
61 for obj_name
, obj
in attrs
.items():
62 new_class
.add_to_class(obj_name
, obj
)
64 # Add Fields inherited from parents
65 for parent
in new_class
._meta
.parents
:
66 for field
in parent
._meta
.fields
:
67 # Only add parent fields if they aren't defined for this class.
69 new_class
._meta
.get_field(field
.name
)
70 except FieldDoesNotExist
:
71 field
.contribute_to_class(new_class
, field
.name
)
75 register_models(new_class
._meta
.app_label
, new_class
)
76 # Because of the way imports happen (recursively), we may or may not be
77 # the first class for this model to register with the framework. There
78 # should only be one class for each model, so we must always return the
80 return get_model(new_class
._meta
.app_label
, name
, False)
82 def add_to_class(cls
, name
, value
):
84 assert type(value
) == types
.ClassType
, "%r attribute of %s model must be a class, not a %s object" % (name
, cls
.__name
__, type(value
))
85 value
= AdminOptions(**dict([(k
, v
) for k
, v
in value
.__dict
__.items() if not k
.startswith('_')]))
86 if hasattr(value
, 'contribute_to_class'):
87 value
.contribute_to_class(cls
, name
)
89 setattr(cls
, name
, value
)
92 # Creates some methods once self._meta has been populated.
96 if opts
.order_with_respect_to
:
97 cls
.get_next_in_order
= curry(cls
._get
_next
_or
_previous
_in
_order
, is_next
=True)
98 cls
.get_previous_in_order
= curry(cls
._get
_next
_or
_previous
_in
_order
, is_next
=False)
99 setattr(opts
.order_with_respect_to
.rel
.to
, 'get_%s_order' % cls
.__name
__.lower(), curry(method_get_order
, cls
))
100 setattr(opts
.order_with_respect_to
.rel
.to
, 'set_%s_order' % cls
.__name
__.lower(), curry(method_set_order
, cls
))
102 # Give the class a docstring -- its definition.
103 if cls
.__doc
__ is None:
104 cls
.__doc
__ = "%s(%s)" % (cls
.__name
__, ", ".join([f
.attname
for f
in opts
.fields
]))
106 if hasattr(cls
, 'get_absolute_url'):
107 cls
.get_absolute_url
= curry(get_absolute_url
, opts
, cls
.get_absolute_url
)
109 dispatcher
.send(signal
=signals
.class_prepared
, sender
=cls
)
112 __metaclass__
= ModelBase
114 def _get_pk_val(self
):
115 return getattr(self
, self
._meta
.pk
.attname
)
117 def _set_pk_val(self
, value
):
118 return setattr(self
, self
._meta
.pk
.attname
, value
)
120 pk
= property(_get_pk_val
, _set_pk_val
)
123 return smart_str(u
'<%s: %s>' % (self
.__class
__.__name
__, unicode(self
)))
126 if hasattr(self
, '__unicode__'):
127 return force_unicode(self
).encode('utf-8')
128 return '%s object' % self
.__class
__.__name
__
130 def __eq__(self
, other
):
131 return isinstance(other
, self
.__class
__) and self
._get
_pk
_val
() == other
._get
_pk
_val
()
133 def __ne__(self
, other
):
134 return not self
.__eq
__(other
)
136 def __init__(self
, *args
, **kwargs
):
137 dispatcher
.send(signal
=signals
.pre_init
, sender
=self
.__class
__, args
=args
, kwargs
=kwargs
)
139 # There is a rather weird disparity here; if kwargs, it's set, then args
140 # overrides it. It should be one or the other; don't duplicate the work
141 # The reason for the kwargs check is that standard iterator passes in by
142 # args, and nstantiation for iteration is 33% faster.
144 if args_len
> len(self
._meta
.fields
):
145 # Daft, but matches old exception sans the err msg.
146 raise IndexError("Number of args exceeds number of fields")
148 fields_iter
= iter(self
._meta
.fields
)
150 # The ordering of the izip calls matter - izip throws StopIteration
151 # when an iter throws it. So if the first iter throws it, the second
152 # is *not* consumed. We rely on this, so don't change the order
153 # without changing the logic.
154 for val
, field
in izip(args
, fields_iter
):
155 setattr(self
, field
.attname
, val
)
157 # Slower, kwargs-ready version.
158 for val
, field
in izip(args
, fields_iter
):
159 setattr(self
, field
.attname
, val
)
160 kwargs
.pop(field
.name
, None)
161 # Maintain compatibility with existing calls.
162 if isinstance(field
.rel
, ManyToOneRel
):
163 kwargs
.pop(field
.attname
, None)
165 # Now we're left with the unprocessed fields that *must* come from
166 # keywords, or default.
168 for field
in fields_iter
:
170 if isinstance(field
.rel
, ManyToOneRel
):
172 # Assume object instance was passed in.
173 rel_obj
= kwargs
.pop(field
.name
)
176 # Object instance wasn't passed in -- must be an ID.
177 val
= kwargs
.pop(field
.attname
)
179 val
= field
.get_default()
181 # Object instance was passed in. Special case: You can
182 # pass in "None" for related objects if it's allowed.
183 if rel_obj
is None and field
.null
:
187 val
= getattr(rel_obj
, field
.rel
.get_related_field().attname
)
188 except AttributeError:
189 raise TypeError("Invalid value: %r should be a %s instance, not a %s" %
190 (field
.name
, field
.rel
.to
, type(rel_obj
)))
192 val
= kwargs
.pop(field
.attname
, field
.get_default())
194 val
= field
.get_default()
195 setattr(self
, field
.attname
, val
)
198 for prop
in kwargs
.keys():
200 if isinstance(getattr(self
.__class
__, prop
), property):
201 setattr(self
, prop
, kwargs
.pop(prop
))
202 except AttributeError:
205 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs
.keys()[0]
206 dispatcher
.send(signal
=signals
.post_init
, sender
=self
.__class
__, instance
=self
)
208 def save(self
, raw
=False):
209 dispatcher
.send(signal
=signals
.pre_save
, sender
=self
.__class
__,
210 instance
=self
, raw
=raw
)
212 non_pks
= [f
for f
in self
._meta
.fields
if not f
.primary_key
]
213 cursor
= connection
.cursor()
215 qn
= connection
.ops
.quote_name
217 # First, try an UPDATE. If that doesn't update anything, do an INSERT.
218 pk_val
= self
._get
_pk
_val
()
219 # Note: the comparison with '' is required for compatibility with
220 # oldforms-style model creation.
221 pk_set
= pk_val
is not None and smart_unicode(pk_val
) != u
''
224 # Determine whether a record with the primary key already exists.
225 cursor
.execute("SELECT 1 FROM %s WHERE %s=%%s" % \
226 (qn(self
._meta
.db_table
), qn(self
._meta
.pk
.column
)),
227 self
._meta
.pk
.get_db_prep_lookup('exact', pk_val
))
228 # If it does already exist, do an UPDATE.
229 if cursor
.fetchone():
230 db_values
= [f
.get_db_prep_save(raw
and getattr(self
, f
.attname
) or f
.pre_save(self
, False)) for f
in non_pks
]
232 cursor
.execute("UPDATE %s SET %s WHERE %s=%%s" % \
233 (qn(self
._meta
.db_table
),
234 ','.join(['%s=%%s' % qn(f
.column
) for f
in non_pks
]),
235 qn(self
._meta
.pk
.column
)),
236 db_values
+ self
._meta
.pk
.get_db_prep_lookup('exact', pk_val
))
238 record_exists
= False
239 if not pk_set
or not record_exists
:
240 field_names
= [qn(f
.column
) for f
in self
._meta
.fields
if not isinstance(f
, AutoField
)]
241 db_values
= [f
.get_db_prep_save(raw
and getattr(self
, f
.attname
) or f
.pre_save(self
, True)) for f
in self
._meta
.fields
if not isinstance(f
, AutoField
)]
242 # If the PK has been manually set, respect that.
244 field_names
+= [f
.column
for f
in self
._meta
.fields
if isinstance(f
, AutoField
)]
245 db_values
+= [f
.get_db_prep_save(raw
and getattr(self
, f
.attname
) or f
.pre_save(self
, True)) for f
in self
._meta
.fields
if isinstance(f
, AutoField
)]
246 placeholders
= ['%s'] * len(field_names
)
247 if self
._meta
.order_with_respect_to
:
248 field_names
.append(qn('_order'))
249 placeholders
.append('%s')
250 subsel
= 'SELECT COUNT(*) FROM %s WHERE %s = %%s' % (
251 qn(self
._meta
.db_table
),
252 qn(self
._meta
.order_with_respect_to
.column
))
253 cursor
.execute(subsel
, (getattr(self
, self
._meta
.order_with_respect_to
.attname
),))
254 db_values
.append(cursor
.fetchone()[0])
255 record_exists
= False
257 cursor
.execute("INSERT INTO %s (%s) VALUES (%s)" % \
258 (qn(self
._meta
.db_table
), ','.join(field_names
),
259 ','.join(placeholders
)), db_values
)
261 # Create a new record with defaults for everything.
262 cursor
.execute("INSERT INTO %s (%s) VALUES (%s)" %
263 (qn(self
._meta
.db_table
), qn(self
._meta
.pk
.column
),
264 connection
.ops
.pk_default_value()))
265 if self
._meta
.has_auto_field
and not pk_set
:
266 setattr(self
, self
._meta
.pk
.attname
, connection
.ops
.last_insert_id(cursor
, self
._meta
.db_table
, self
._meta
.pk
.column
))
267 transaction
.commit_unless_managed()
269 # Run any post-save hooks.
270 dispatcher
.send(signal
=signals
.post_save
, sender
=self
.__class
__,
271 instance
=self
, created
=(not record_exists
), raw
=raw
)
273 save
.alters_data
= True
277 First coerces all fields on this instance to their proper Python types.
278 Then runs validation on every field. Returns a dictionary of
279 field_name -> error_list.
283 for f
in self
._meta
.fields
:
285 setattr(self
, f
.attname
, f
.to_python(getattr(self
, f
.attname
, f
.get_default())))
286 except validators
.ValidationError
, e
:
287 error_dict
[f
.name
] = e
.messages
288 invalid_python
[f
.name
] = 1
289 for f
in self
._meta
.fields
:
290 if f
.name
in invalid_python
:
292 errors
= f
.validate_full(getattr(self
, f
.attname
, f
.get_default()), self
.__dict
__)
294 error_dict
[f
.name
] = errors
297 def _collect_sub_objects(self
, seen_objs
):
299 Recursively populates seen_objs with all objects related to this object.
300 When done, seen_objs will be in the format:
301 {model_class: {pk_val: obj, pk_val: obj, ...},
302 model_class: {pk_val: obj, pk_val: obj, ...}, ...}
304 pk_val
= self
._get
_pk
_val
()
305 if pk_val
in seen_objs
.setdefault(self
.__class
__, {}):
307 seen_objs
.setdefault(self
.__class
__, {})[pk_val
] = self
309 for related
in self
._meta
.get_all_related_objects():
310 rel_opts_name
= related
.get_accessor_name()
311 if isinstance(related
.field
.rel
, OneToOneRel
):
313 sub_obj
= getattr(self
, rel_opts_name
)
314 except ObjectDoesNotExist
:
317 sub_obj
._collect
_sub
_objects
(seen_objs
)
319 for sub_obj
in getattr(self
, rel_opts_name
).all():
320 sub_obj
._collect
_sub
_objects
(seen_objs
)
323 assert self
._get
_pk
_val
() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self
._meta
.object_name
, self
._meta
.pk
.attname
)
325 # Find all the objects than need to be deleted
326 seen_objs
= SortedDict()
327 self
._collect
_sub
_objects
(seen_objs
)
329 # Actually delete the objects
330 delete_objects(seen_objs
)
332 delete
.alters_data
= True
334 def _get_FIELD_display(self
, field
):
335 value
= getattr(self
, field
.attname
)
336 return force_unicode(dict(field
.choices
).get(value
, value
), strings_only
=True)
338 def _get_next_or_previous_by_FIELD(self
, field
, is_next
, **kwargs
):
339 qn
= connection
.ops
.quote_name
340 op
= is_next
and '>' or '<'
341 where
= '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \
342 (qn(field
.column
), op
, qn(field
.column
),
343 qn(self
._meta
.db_table
), qn(self
._meta
.pk
.column
), op
)
344 param
= smart_str(getattr(self
, field
.attname
))
345 q
= self
.__class
__._default
_manager
.filter(**kwargs
).order_by((not is_next
and '-' or '') + field
.name
, (not is_next
and '-' or '') + self
._meta
.pk
.name
)
346 q
._where
.append(where
)
347 q
._params
.extend([param
, param
, getattr(self
, self
._meta
.pk
.attname
)])
351 raise self
.DoesNotExist
, "%s matching query does not exist." % self
.__class
__._meta
.object_name
353 def _get_next_or_previous_in_order(self
, is_next
):
354 qn
= connection
.ops
.quote_name
355 cachename
= "__%s_order_cache" % is_next
356 if not hasattr(self
, cachename
):
357 op
= is_next
and '>' or '<'
358 order_field
= self
._meta
.order_with_respect_to
359 where
= ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
360 (qn('_order'), op
, qn('_order'),
361 qn(self
._meta
.db_table
), qn(self
._meta
.pk
.column
)),
362 '%s=%%s' % qn(order_field
.column
)]
363 params
= [self
._get
_pk
_val
(), getattr(self
, order_field
.attname
)]
364 obj
= self
._default
_manager
.order_by('_order').extra(where
=where
, params
=params
)[:1].get()
365 setattr(self
, cachename
, obj
)
366 return getattr(self
, cachename
)
368 def _get_FIELD_filename(self
, field
):
369 if getattr(self
, field
.attname
): # value is not blank
370 return os
.path
.join(settings
.MEDIA_ROOT
, getattr(self
, field
.attname
))
373 def _get_FIELD_url(self
, field
):
374 if getattr(self
, field
.attname
): # value is not blank
376 return urlparse
.urljoin(settings
.MEDIA_URL
, getattr(self
, field
.attname
)).replace('\\', '/')
379 def _get_FIELD_size(self
, field
):
380 return os
.path
.getsize(self
._get
_FIELD
_filename
(field
))
382 def _save_FIELD_file(self
, field
, filename
, raw_contents
, save
=True):
383 directory
= field
.get_directory_name()
384 try: # Create the date-based directory if it doesn't exist.
385 os
.makedirs(os
.path
.join(settings
.MEDIA_ROOT
, directory
))
386 except OSError: # Directory probably already exists.
388 filename
= field
.get_filename(filename
)
390 # If the filename already exists, keep adding an underscore to the name of
391 # the file until the filename doesn't exist.
392 while os
.path
.exists(os
.path
.join(settings
.MEDIA_ROOT
, filename
)):
394 dot_index
= filename
.rindex('.')
395 except ValueError: # filename has no dot
398 filename
= filename
[:dot_index
] + '_' + filename
[dot_index
:]
400 # Write the file to disk.
401 setattr(self
, field
.attname
, filename
)
403 full_filename
= self
._get
_FIELD
_filename
(field
)
404 fp
= open(full_filename
, 'wb')
405 fp
.write(raw_contents
)
408 # Save the width and/or height, if applicable.
409 if isinstance(field
, ImageField
) and (field
.width_field
or field
.height_field
):
410 from django
.utils
.images
import get_image_dimensions
411 width
, height
= get_image_dimensions(full_filename
)
412 if field
.width_field
:
413 setattr(self
, field
.width_field
, width
)
414 if field
.height_field
:
415 setattr(self
, field
.height_field
, height
)
417 # Save the object because it has changed unless save is False
421 _save_FIELD_file
.alters_data
= True
423 def _get_FIELD_width(self
, field
):
424 return self
._get
_image
_dimensions
(field
)[0]
426 def _get_FIELD_height(self
, field
):
427 return self
._get
_image
_dimensions
(field
)[1]
429 def _get_image_dimensions(self
, field
):
430 cachename
= "__%s_dimensions_cache" % field
.name
431 if not hasattr(self
, cachename
):
432 from django
.utils
.images
import get_image_dimensions
433 filename
= self
._get
_FIELD
_filename
(field
)
434 setattr(self
, cachename
, get_image_dimensions(filename
))
435 return getattr(self
, cachename
)
437 ############################################
438 # HELPER FUNCTIONS (CURRIED MODEL METHODS) #
439 ############################################
441 # ORDERING METHODS #########################
443 def method_set_order(ordered_obj
, self
, id_list
):
444 qn
= connection
.ops
.quote_name
445 cursor
= connection
.cursor()
446 # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s"
447 sql
= "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \
448 (qn(ordered_obj
._meta
.db_table
), qn('_order'),
449 qn(ordered_obj
._meta
.order_with_respect_to
.column
),
450 qn(ordered_obj
._meta
.pk
.column
))
451 rel_val
= getattr(self
, ordered_obj
._meta
.order_with_respect_to
.rel
.field_name
)
452 cursor
.executemany(sql
, [(i
, rel_val
, j
) for i
, j
in enumerate(id_list
)])
453 transaction
.commit_unless_managed()
455 def method_get_order(ordered_obj
, self
):
456 qn
= connection
.ops
.quote_name
457 cursor
= connection
.cursor()
458 # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order"
459 sql
= "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \
460 (qn(ordered_obj
._meta
.pk
.column
),
461 qn(ordered_obj
._meta
.db_table
),
462 qn(ordered_obj
._meta
.order_with_respect_to
.column
),
464 rel_val
= getattr(self
, ordered_obj
._meta
.order_with_respect_to
.rel
.field_name
)
465 cursor
.execute(sql
, [rel_val
])
466 return [r
[0] for r
in cursor
.fetchall()]
468 ##############################################
469 # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) #
470 ##############################################
472 def get_absolute_url(opts
, func
, self
, *args
, **kwargs
):
473 return settings
.ABSOLUTE_URL_OVERRIDES
.get('%s.%s' % (opts
.app_label
, opts
.module_name
), func
)(self
, *args
, **kwargs
)