diff --git a/django/db/models/base.py b/django/db/models/base.py index e881c32ebc..338b0618e0 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -3,6 +3,10 @@ import types import sys import os from itertools import izip +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback. import django.db.models.manipulators # Imported to register signal handler. import django.db.models.manager # Ditto. @@ -23,13 +27,11 @@ from django.core.files.move import file_move_safe from django.core.files import locks from django.conf import settings -try: - set -except NameError: - from sets import Set as set # Python 2.3 fallback class ModelBase(type): - "Metaclass for all models" + """ + Metaclass for all models. + """ def __new__(cls, name, bases, attrs): super_new = super(ModelBase, cls).__new__ parents = [b for b in bases if isinstance(b, ModelBase)] @@ -144,7 +146,9 @@ class ModelBase(type): setattr(cls, name, value) def _prepare(cls): - # Creates some methods once self._meta has been populated. + """ + Creates some methods once self._meta has been populated. + """ opts = cls._meta opts._prepare(cls) @@ -163,6 +167,7 @@ class ModelBase(type): dispatcher.send(signal=signals.class_prepared, sender=cls) + class Model(object): __metaclass__ = ModelBase @@ -267,7 +272,7 @@ class Model(object): def save(self): """ - Save the current instance. Override this in a subclass if you want to + Saves the current instance. Override this in a subclass if you want to control the saving process. """ self.save_base() @@ -293,7 +298,7 @@ class Model(object): # If we are in a raw save, save the object exactly as presented. # That means that we don't try to be smart about saving attributes - # that might have come from the parent class - we just save the + # that might have come from the parent class - we just save the # attributes we have been given to the class we have been given. if not raw: for parent, field in meta.parents.items(): @@ -301,7 +306,7 @@ class Model(object): setattr(self, field.attname, self._get_pk_val(parent._meta)) non_pks = [f for f in meta.local_fields if not f.primary_key] - + # First, try an UPDATE. If that doesn't update anything, do an INSERT. pk_val = self._get_pk_val(meta) # Note: the comparison with '' is required for compatibility with @@ -371,10 +376,12 @@ class Model(object): def _collect_sub_objects(self, seen_objs, parent=None, nullable=False): """ - Recursively populates seen_objs with all objects related to this object. + Recursively populates seen_objs with all objects related to this + object. + When done, seen_objs.items() will be in the format: [(model_class, {pk_val: obj, pk_val: obj, ...}), - (model_class, {pk_val: obj, pk_val: obj, ...}),...] + (model_class, {pk_val: obj, pk_val: obj, ...}), ...] """ pk_val = self._get_pk_val() if seen_objs.add(self.__class__, pk_val, self, parent, nullable): @@ -411,11 +418,11 @@ class Model(object): def delete(self): 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) - # Find all the objects than need to be deleted + # Find all the objects than need to be deleted. seen_objs = CollectedObjects() self._collect_sub_objects(seen_objs) - # Actually delete the objects + # Actually delete the objects. delete_objects(seen_objs) delete.alters_data = True @@ -454,12 +461,12 @@ class Model(object): return getattr(self, cachename) def _get_FIELD_filename(self, field): - if getattr(self, field.attname): # value is not blank + if getattr(self, field.attname): # Value is not blank. return os.path.normpath(os.path.join(settings.MEDIA_ROOT, getattr(self, field.attname))) return '' def _get_FIELD_url(self, field): - if getattr(self, field.attname): # value is not blank + if getattr(self, field.attname): # Value is not blank. import urlparse return urlparse.urljoin(settings.MEDIA_URL, getattr(self, field.attname)).replace('\\', '/') return '' @@ -474,16 +481,14 @@ class Model(object): except OSError: # Directory probably already exists. pass - # # Check for old-style usage (files-as-dictionaries). Warn here first # since there are multiple locations where we need to support both new # and old usage. - # if isinstance(raw_field, dict): import warnings warnings.warn( message = "Representing uploaded files as dictionaries is"\ - " deprected. Use django.core.files.SimpleUploadedFile"\ + " deprecated. Use django.core.files.SimpleUploadedFile"\ " instead.", category = DeprecationWarning, stacklevel = 2 @@ -508,30 +513,23 @@ class Model(object): 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. - # + # 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 + except ValueError: # filename has no dot. filename += '_' else: filename = filename[:dot_index] + '_' + filename[dot_index:] - # - # Save the file name on the object and write the file to disk - # + # Save the file name on the object and write the file to disk. setattr(self, field.attname, filename) - full_filename = self._get_FIELD_filename(field) - if hasattr(raw_field, 'temporary_file_path'): # This file has a file path that we can move. raw_field.close() file_move_safe(raw_field.temporary_file_path(), full_filename) - else: # This is a normal uploadedfile that we can stream. fp = open(full_filename, 'wb') @@ -542,7 +540,8 @@ class Model(object): fp.close() # Save the width and/or height, if applicable. - if isinstance(field, ImageField) and (field.width_field or field.height_field): + 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(full_filename) if field.width_field: @@ -550,7 +549,7 @@ class Model(object): if field.height_field: setattr(self, field.height_field, height) - # Save the object because it has changed unless save is False + # Save the object because it has changed, unless save is False. if save: self.save() @@ -570,6 +569,7 @@ class Model(object): setattr(self, cachename, get_image_dimensions(filename)) return getattr(self, cachename) + ############################################ # HELPER FUNCTIONS (CURRIED MODEL METHODS) # ############################################ @@ -585,6 +585,7 @@ def method_set_order(ordered_obj, self, id_list): ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i) transaction.commit_unless_managed() + def method_get_order(ordered_obj, self): rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name) order_name = ordered_obj._meta.order_with_respect_to.name @@ -592,6 +593,7 @@ def method_get_order(ordered_obj, self): return [r[pk_name] for r in ordered_obj.objects.filter(**{order_name: rel_val}).values(pk_name)] + ############################################## # HELPER FUNCTIONS (CURRIED MODEL FUNCTIONS) # ############################################## @@ -599,6 +601,7 @@ def method_get_order(ordered_obj, self): def get_absolute_url(opts, func, self, *args, **kwargs): return settings.ABSOLUTE_URL_OVERRIDES.get('%s.%s' % (opts.app_label, opts.module_name), func)(self, *args, **kwargs) + ######## # MISC # ######## @@ -610,8 +613,6 @@ if sys.version_info < (2, 5): # Prior to Python 2.5, Exception was an old-style class def subclass_exception(name, parent, unused): return types.ClassType(name, (parent,), {}) - else: def subclass_exception(name, parent, module): return type(name, (parent,), {'__module__': module}) - diff --git a/django/db/models/query.py b/django/db/models/query.py index e92f6c4275..986846fc3a 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -13,19 +13,20 @@ from django.utils.datastructures import SortedDict CHUNK_SIZE = 100 ITER_CHUNK_SIZE = CHUNK_SIZE -# Pull into this namespace for backwards compatibility +# Pull into this namespace for backwards compatibility. EmptyResultSet = sql.EmptyResultSet class CyclicDependency(Exception): pass + class CollectedObjects(object): """ - A container that stores keys and lists of values along with - remembering the parent objects for all the keys. + A container that stores keys and lists of values along with remembering the + parent objects for all the keys. - This is used for the database object deletion routines so that we - can calculate the 'leaf' objects which should be deleted first. + This is used for the database object deletion routines so that we can + calculate the 'leaf' objects which should be deleted first. """ def __init__(self): @@ -34,26 +35,27 @@ class CollectedObjects(object): def add(self, model, pk, obj, parent_model, nullable=False): """ - Adds an item. - model is the class of the object being added, - pk is the primary key, obj is the object itself, - parent_model is the model of the parent object - that this object was reached through, nullable should - be True if this relation is nullable. + Adds an item to the container. - If the item already existed in the structure, - returns true, otherwise false. + Arguments: + * model - the class of the object being added. + * pk - the primary key. + * obj - the object itself. + * parent_model - the model of the parent object that this object was + reached through. + * nullable - should be True if this relation is nullable. + + Returns True if the item already existed in the structure and + False otherwise. """ d = self.data.setdefault(model, SortedDict()) retval = pk in d d[pk] = obj - # Nullable relationships can be ignored -- they - # are nulled out before deleting, and therefore - # do not affect the order in which objects have - # to be deleted. + # Nullable relationships can be ignored -- they are nulled out before + # deleting, and therefore do not affect the order in which objects + # have to be deleted. if parent_model is not None and not nullable: self.children.setdefault(parent_model, []).append(model) - return retval def __contains__(self, key): @@ -77,8 +79,8 @@ class CollectedObjects(object): def ordered_keys(self): """ - Returns the models in the order that they should be - dealth with i.e. models with no dependencies first. + Returns the models in the order that they should be dealt with (i.e. + models with no dependencies first). """ dealt_with = SortedDict() # Start with items that have no children @@ -91,19 +93,22 @@ class CollectedObjects(object): dealt_with[model] = None found = True if not found: - raise CyclicDependency("There is a cyclic dependency of items to be processed.") + raise CyclicDependency( + "There is a cyclic dependency of items to be processed.") return dealt_with.keys() def unordered_keys(self): """ - Fallback for the case where is a cyclic dependency but we - don't care. + Fallback for the case where is a cyclic dependency but we don't care. """ return self.data.keys() + class QuerySet(object): - "Represents a lazy database lookup for a set of objects" + """ + Represents a lazy database lookup for a set of objects. + """ def __init__(self, model=None, query=None): self.model = model self.query = query or sql.Query(self.model, connection) @@ -116,7 +121,7 @@ class QuerySet(object): def __getstate__(self): """ - Allows the Queryset to be pickled. + Allows the QuerySet to be pickled. """ # Force the cache to be fully populated. len(self) @@ -131,7 +136,7 @@ class QuerySet(object): def __len__(self): # Since __len__ is called quite frequently (for example, as part of # list(qs), we make some effort here to be as efficient as possible - # whilst not messing up any existing iterators against the queryset. + # whilst not messing up any existing iterators against the QuerySet. if self._result_cache is None: if self._iter: self._result_cache = list(self._iter) @@ -173,7 +178,9 @@ class QuerySet(object): return True def __getitem__(self, k): - "Retrieve an item or slice from the set of results." + """ + Retrieves an item or slice from the set of results. + """ if not isinstance(k, (slice, int, long)): raise TypeError assert ((not isinstance(k, slice) and (k >= 0)) @@ -264,7 +271,7 @@ class QuerySet(object): Performs a SELECT COUNT() and returns the number of records as an integer. - If the queryset is already cached (i.e. self._result_cache is set) this + If the QuerySet is already cached (i.e. self._result_cache is set) this simply returns the length of the cached results set to avoid multiple SELECT COUNT(*) calls. """ @@ -290,7 +297,7 @@ class QuerySet(object): def create(self, **kwargs): """ - Create a new object with the given kwargs, saving it to the database + Creates a new object with the given kwargs, saving it to the database and returning the created object. """ obj = self.model(**kwargs) @@ -425,8 +432,8 @@ class QuerySet(object): def dates(self, field_name, kind, order='ASC'): """ - Returns a list of datetime objects representing all available dates - for the given field_name, scoped to 'kind'. + Returns a list of datetime objects representing all available dates for + the given field_name, scoped to 'kind'. """ assert kind in ("month", "year", "day"), \ "'kind' must be one of 'year', 'month' or 'day'." @@ -441,7 +448,7 @@ class QuerySet(object): def none(self): """ - Returns an empty queryset. + Returns an empty QuerySet. """ return self._clone(klass=EmptyQuerySet) @@ -485,6 +492,7 @@ class QuerySet(object): def complex_filter(self, filter_obj): """ Returns a new QuerySet instance with filter_obj added to the filters. + filter_obj can be a Q object (or anything with an add_to_query() method) or a dictionary of keyword lookup arguments. @@ -500,8 +508,9 @@ class QuerySet(object): def select_related(self, *fields, **kwargs): """ - Returns a new QuerySet instance that will select related objects. If - fields are specified, they must be ForeignKey fields and only those + Returns a new QuerySet instance that will select related objects. + + If fields are specified, they must be ForeignKey fields and only those related objects are included in the selection. """ depth = kwargs.pop('depth', 0) @@ -521,13 +530,15 @@ class QuerySet(object): def dup_select_related(self, other): """ - Copies the related selection status from the queryset 'other' to the - current queryset. + Copies the related selection status from the QuerySet 'other' to the + current QuerySet. """ self.query.select_related = other.query.select_related def order_by(self, *field_names): - """Returns a new QuerySet instance with the ordering changed.""" + """ + Returns a new QuerySet instance with the ordering changed. + """ assert self.query.can_filter(), \ "Cannot reorder a query once a slice has been taken." obj = self._clone() @@ -544,9 +555,9 @@ class QuerySet(object): return obj def extra(self, select=None, where=None, params=None, tables=None, - order_by=None, select_params=None): + order_by=None, select_params=None): """ - Add extra SQL fragments to the query. + Adds extra SQL fragments to the query. """ assert self.query.can_filter(), \ "Cannot change a query once a slice has been taken" @@ -556,7 +567,7 @@ class QuerySet(object): def reverse(self): """ - Reverses the ordering of the queryset. + Reverses the ordering of the QuerySet. """ clone = self._clone() clone.query.standard_ordering = not clone.query.standard_ordering @@ -589,12 +600,13 @@ class QuerySet(object): def _merge_sanity_check(self, other): """ - Checks that we are merging two comparable queryset classes. By default + Checks that we are merging two comparable QuerySet classes. By default this does nothing, but see the ValuesQuerySet for an example of where it's useful. """ pass + class ValuesQuerySet(QuerySet): def __init__(self, *args, **kwargs): super(ValuesQuerySet, self).__init__(*args, **kwargs) @@ -617,7 +629,7 @@ class ValuesQuerySet(QuerySet): Constructs the field_names list that the values query will be retrieving. - Called by the _clone() method after initialising the rest of the + Called by the _clone() method after initializing the rest of the instance. """ self.extra_names = [] @@ -658,6 +670,7 @@ class ValuesQuerySet(QuerySet): raise TypeError("Merging '%s' classes must involve the same values in each case." % self.__class__.__name__) + class ValuesListQuerySet(ValuesQuerySet): def iterator(self): self.query.trim_extra_select(self.extra_names) @@ -681,6 +694,7 @@ class ValuesListQuerySet(ValuesQuerySet): clone.flat = self.flat return clone + class DateQuerySet(QuerySet): def iterator(self): return self.query.results_iter() @@ -689,7 +703,7 @@ class DateQuerySet(QuerySet): """ Sets up any special features of the query attribute. - Called by the _clone() method after initialising the rest of the + Called by the _clone() method after initializing the rest of the instance. """ self.query = self.query.clone(klass=sql.DateQuery, setup=True) @@ -706,6 +720,7 @@ class DateQuerySet(QuerySet): c._setup_query() return c + class EmptyQuerySet(QuerySet): def __init__(self, model=None, query=None): super(EmptyQuerySet, self).__init__(model, query) @@ -733,6 +748,7 @@ class EmptyQuerySet(QuerySet): # (it raises StopIteration immediately). yield iter([]).next() + # QOperator, QNot, QAnd and QOr are temporarily retained for backwards # compatibility. All the old functionality is now part of the 'Q' class. class QOperator(Q): @@ -743,12 +759,14 @@ class QOperator(Q): QOr = QAnd = QOperator + def QNot(q): warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2) return ~q + def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, - requested=None): + requested=None): """ Helper function that recursively returns an object with the specified related attributes already populated. @@ -774,6 +792,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, setattr(obj, f.get_cache_name(), rel_obj) return obj, index_end + def delete_objects(seen_objs): """ Iterate through a list of seen classes, and remove any instances that are @@ -782,10 +801,10 @@ def delete_objects(seen_objs): try: ordered_classes = seen_objs.keys() except CyclicDependency: - # if there is a cyclic dependency, we cannot in general delete - # the objects. However, if an appropriate transaction is set - # up, or if the database is lax enough, it will succeed. - # So for now, we go ahead and try anway. + # If there is a cyclic dependency, we cannot in general delete the + # objects. However, if an appropriate transaction is set up, or if the + # database is lax enough, it will succeed. So for now, we go ahead and + # try anyway. ordered_classes = seen_objs.unordered_keys() obj_pairs = {} @@ -794,7 +813,7 @@ def delete_objects(seen_objs): items.sort() obj_pairs[cls] = items - # Pre notify all instances to be deleted + # Pre-notify all instances to be deleted. for pk_val, instance in items: dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance) @@ -808,7 +827,7 @@ def delete_objects(seen_objs): if field.rel and field.null and field.rel.to in seen_objs: update_query.clear_related(field, pk_list) - # Now delete the actual data + # Now delete the actual data. for cls in ordered_classes: items = obj_pairs[cls] items.reverse() @@ -831,6 +850,7 @@ def delete_objects(seen_objs): transaction.commit_unless_managed() + def insert_query(model, values, return_id=False, raw_values=False): """ Inserts a new record for the given model. This provides an interface to @@ -840,4 +860,3 @@ def insert_query(model, values, return_id=False, raw_values=False): query = sql.InsertQuery(model, connection) query.insert_values(values, raw_values) return query.execute_sql(return_id) -