web/lib/django/db/models/query_utils.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 """
       
     2 Various data structures used in query construction.
       
     3 
       
     4 Factored out from django.db.models.query to avoid making the main module very
       
     5 large and/or so that they can be used by other modules without getting into
       
     6 circular import difficulties.
       
     7 """
       
     8 
       
     9 import weakref
       
    10 from django.utils.copycompat import deepcopy
       
    11 
       
    12 from django.utils import tree
       
    13 from django.utils.datastructures import SortedDict
       
    14 
       
    15 
       
    16 class CyclicDependency(Exception):
       
    17     """
       
    18     An error when dealing with a collection of objects that have a cyclic
       
    19     dependency, i.e. when deleting multiple objects.
       
    20     """
       
    21     pass
       
    22 
       
    23 class InvalidQuery(Exception):
       
    24     """
       
    25     The query passed to raw isn't a safe query to use with raw.
       
    26     """
       
    27     pass
       
    28 
       
    29 
       
    30 class CollectedObjects(object):
       
    31     """
       
    32     A container that stores keys and lists of values along with remembering the
       
    33     parent objects for all the keys.
       
    34 
       
    35     This is used for the database object deletion routines so that we can
       
    36     calculate the 'leaf' objects which should be deleted first.
       
    37 
       
    38     previously_seen is an optional argument. It must be a CollectedObjects
       
    39     instance itself; any previously_seen collected object will be blocked from
       
    40     being added to this instance.
       
    41     """
       
    42 
       
    43     def __init__(self, previously_seen=None):
       
    44         self.data = {}
       
    45         self.children = {}
       
    46         if previously_seen:
       
    47             self.blocked = previously_seen.blocked
       
    48             for cls, seen in previously_seen.data.items():
       
    49                 self.blocked.setdefault(cls, SortedDict()).update(seen)
       
    50         else:
       
    51             self.blocked = {}
       
    52 
       
    53     def add(self, model, pk, obj, parent_model, parent_obj=None, nullable=False):
       
    54         """
       
    55         Adds an item to the container.
       
    56 
       
    57         Arguments:
       
    58         * model - the class of the object being added.
       
    59         * pk - the primary key.
       
    60         * obj - the object itself.
       
    61         * parent_model - the model of the parent object that this object was
       
    62           reached through.
       
    63         * parent_obj - the parent object this object was reached
       
    64           through (not used here, but needed in the API for use elsewhere)
       
    65         * nullable - should be True if this relation is nullable.
       
    66 
       
    67         Returns True if the item already existed in the structure and
       
    68         False otherwise.
       
    69         """
       
    70         if pk in self.blocked.get(model, {}):
       
    71             return True
       
    72 
       
    73         d = self.data.setdefault(model, SortedDict())
       
    74         retval = pk in d
       
    75         d[pk] = obj
       
    76         # Nullable relationships can be ignored -- they are nulled out before
       
    77         # deleting, and therefore do not affect the order in which objects
       
    78         # have to be deleted.
       
    79         if parent_model is not None and not nullable:
       
    80             self.children.setdefault(parent_model, []).append(model)
       
    81         return retval
       
    82 
       
    83     def __contains__(self, key):
       
    84         return self.data.__contains__(key)
       
    85 
       
    86     def __getitem__(self, key):
       
    87         return self.data[key]
       
    88 
       
    89     def __nonzero__(self):
       
    90         return bool(self.data)
       
    91 
       
    92     def iteritems(self):
       
    93         for k in self.ordered_keys():
       
    94             yield k, self[k]
       
    95 
       
    96     def items(self):
       
    97         return list(self.iteritems())
       
    98 
       
    99     def keys(self):
       
   100         return self.ordered_keys()
       
   101 
       
   102     def ordered_keys(self):
       
   103         """
       
   104         Returns the models in the order that they should be dealt with (i.e.
       
   105         models with no dependencies first).
       
   106         """
       
   107         dealt_with = SortedDict()
       
   108         # Start with items that have no children
       
   109         models = self.data.keys()
       
   110         while len(dealt_with) < len(models):
       
   111             found = False
       
   112             for model in models:
       
   113                 if model in dealt_with:
       
   114                     continue
       
   115                 children = self.children.setdefault(model, [])
       
   116                 if len([c for c in children if c not in dealt_with]) == 0:
       
   117                     dealt_with[model] = None
       
   118                     found = True
       
   119             if not found:
       
   120                 raise CyclicDependency(
       
   121                     "There is a cyclic dependency of items to be processed.")
       
   122 
       
   123         return dealt_with.keys()
       
   124 
       
   125     def unordered_keys(self):
       
   126         """
       
   127         Fallback for the case where is a cyclic dependency but we don't  care.
       
   128         """
       
   129         return self.data.keys()
       
   130 
       
   131 class QueryWrapper(object):
       
   132     """
       
   133     A type that indicates the contents are an SQL fragment and the associate
       
   134     parameters. Can be used to pass opaque data to a where-clause, for example.
       
   135     """
       
   136     def __init__(self, sql, params):
       
   137         self.data = sql, params
       
   138 
       
   139     def as_sql(self, qn=None, connection=None):
       
   140         return self.data
       
   141 
       
   142 class Q(tree.Node):
       
   143     """
       
   144     Encapsulates filters as objects that can then be combined logically (using
       
   145     & and |).
       
   146     """
       
   147     # Connection types
       
   148     AND = 'AND'
       
   149     OR = 'OR'
       
   150     default = AND
       
   151 
       
   152     def __init__(self, *args, **kwargs):
       
   153         super(Q, self).__init__(children=list(args) + kwargs.items())
       
   154 
       
   155     def _combine(self, other, conn):
       
   156         if not isinstance(other, Q):
       
   157             raise TypeError(other)
       
   158         obj = type(self)()
       
   159         obj.add(self, conn)
       
   160         obj.add(other, conn)
       
   161         return obj
       
   162 
       
   163     def __or__(self, other):
       
   164         return self._combine(other, self.OR)
       
   165 
       
   166     def __and__(self, other):
       
   167         return self._combine(other, self.AND)
       
   168 
       
   169     def __invert__(self):
       
   170         obj = type(self)()
       
   171         obj.add(self, self.AND)
       
   172         obj.negate()
       
   173         return obj
       
   174 
       
   175 class DeferredAttribute(object):
       
   176     """
       
   177     A wrapper for a deferred-loading field. When the value is read from this
       
   178     object the first time, the query is executed.
       
   179     """
       
   180     def __init__(self, field_name, model):
       
   181         self.field_name = field_name
       
   182         self.model_ref = weakref.ref(model)
       
   183         self.loaded = False
       
   184 
       
   185     def __get__(self, instance, owner):
       
   186         """
       
   187         Retrieves and caches the value from the datastore on the first lookup.
       
   188         Returns the cached value.
       
   189         """
       
   190         from django.db.models.fields import FieldDoesNotExist
       
   191 
       
   192         assert instance is not None
       
   193         cls = self.model_ref()
       
   194         data = instance.__dict__
       
   195         if data.get(self.field_name, self) is self:
       
   196             # self.field_name is the attname of the field, but only() takes the
       
   197             # actual name, so we need to translate it here.
       
   198             try:
       
   199                 cls._meta.get_field_by_name(self.field_name)
       
   200                 name = self.field_name
       
   201             except FieldDoesNotExist:
       
   202                 name = [f.name for f in cls._meta.fields
       
   203                     if f.attname == self.field_name][0]
       
   204             # We use only() instead of values() here because we want the
       
   205             # various data coersion methods (to_python(), etc.) to be called
       
   206             # here.
       
   207             val = getattr(
       
   208                 cls._base_manager.filter(pk=instance.pk).only(name).using(
       
   209                     instance._state.db).get(),
       
   210                 self.field_name
       
   211             )
       
   212             data[self.field_name] = val
       
   213         return data[self.field_name]
       
   214 
       
   215     def __set__(self, instance, value):
       
   216         """
       
   217         Deferred loading attributes can be set normally (which means there will
       
   218         never be a database lookup involved.
       
   219         """
       
   220         instance.__dict__[self.field_name] = value
       
   221 
       
   222 def select_related_descend(field, restricted, requested, reverse=False):
       
   223     """
       
   224     Returns True if this field should be used to descend deeper for
       
   225     select_related() purposes. Used by both the query construction code
       
   226     (sql.query.fill_related_selections()) and the model instance creation code
       
   227     (query.get_cached_row()).
       
   228 
       
   229     Arguments:
       
   230      * field - the field to be checked
       
   231      * restricted - a boolean field, indicating if the field list has been
       
   232        manually restricted using a requested clause)
       
   233      * requested - The select_related() dictionary.
       
   234      * reverse - boolean, True if we are checking a reverse select related
       
   235     """
       
   236     if not field.rel:
       
   237         return False
       
   238     if field.rel.parent_link and not reverse:
       
   239         return False
       
   240     if restricted:
       
   241         if reverse and field.related_query_name() not in requested:
       
   242             return False
       
   243         if not reverse and field.name not in requested:
       
   244             return False
       
   245     if not restricted and field.null:
       
   246         return False
       
   247     return True
       
   248 
       
   249 # This function is needed because data descriptors must be defined on a class
       
   250 # object, not an instance, to have any effect.
       
   251 
       
   252 def deferred_class_factory(model, attrs):
       
   253     """
       
   254     Returns a class object that is a copy of "model" with the specified "attrs"
       
   255     being replaced with DeferredAttribute objects. The "pk_value" ties the
       
   256     deferred attributes to a particular instance of the model.
       
   257     """
       
   258     class Meta:
       
   259         pass
       
   260     setattr(Meta, "proxy", True)
       
   261     setattr(Meta, "app_label", model._meta.app_label)
       
   262 
       
   263     # The app_cache wants a unique name for each model, otherwise the new class
       
   264     # won't be created (we get an old one back). Therefore, we generate the
       
   265     # name using the passed in attrs. It's OK to reuse an old case if the attrs
       
   266     # are identical.
       
   267     name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs))))
       
   268 
       
   269     overrides = dict([(attr, DeferredAttribute(attr, model))
       
   270             for attr in attrs])
       
   271     overrides["Meta"] = Meta
       
   272     overrides["__module__"] = model.__module__
       
   273     overrides["_deferred"] = True
       
   274     return type(name, (model,), overrides)
       
   275 
       
   276 # The above function is also used to unpickle model instances with deferred
       
   277 # fields.
       
   278 deferred_class_factory.__safe_for_unpickling__ = True