Commits

Al...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed d3f5e1f

[soc2009/multidb] Cleaned up the interaction between managers and the using() method by the addition of a db_manager() method.

  • Participants
  • Parent commits ca9f005
  • Branches soc2009/multidb

Comments (0)

Files changed (13)

  * Modify the admin interface to support multiple databases (doh).
     - Document how it is done
 
- * Make sure we can't get rid of using= arguments everywhere
-
  * Resolve uses of _default_manager
     * django/contrib/databrowse/datastructures.py
     * django/contrib/databrowse/fieldchoices.py

File django/contrib/auth/models.py

 from django.utils.hashcompat import md5_constructor, sha_constructor
 from django.utils.translation import ugettext_lazy as _
 
+
 UNUSABLE_PASSWORD = '!' # This will never be a valid hash
 
-try:
-    set
-except NameError:
-    from sets import Set as set   # Python 2.3 fallback
-
 def get_hexdigest(algorithm, salt, raw_password):
     """
     Returns a string of the hexdigest of the given plaintext password and salt
     pass
 
 class PermissionManager(models.Manager):
-    def get_by_natural_key(self, codename, app_label, model, using=None):
-        return self.using(using).get(
+    def get_by_natural_key(self, codename, app_label, model):
+        return self.get(
             codename=codename,
             content_type=ContentType.objects.get_by_natural_key(app_label, model)
         )
         return self.name
 
 class UserManager(models.Manager):
-    def create_user(self, username, email, password=None, using=None):
+    def create_user(self, username, email, password=None):
         "Creates and saves a User with the given username, e-mail and password."
         now = datetime.datetime.now()
         user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
             user.set_password(password)
         else:
             user.set_unusable_password()
-        user.save(using=using)
+        user.save(using=self.db)
         return user
 
-    def create_superuser(self, username, email, password, using=None):
+    def create_superuser(self, username, email, password):
         u = self.create_user(username, email, password)
         u.is_staff = True
         u.is_active = True
         u.is_superuser = True
-        u.save(using=using)
+        u.save(using=self.db)
         return u
 
     def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'):

File django/contrib/contenttypes/generic.py

         # using this model
         ContentType = get_model("contenttypes", "contenttype")
         if obj:
-             return ContentType.objects.get_for_model(obj, using=obj._state.db)
+             return ContentType.objects.db_manager(obj._state.db).get_for_model(obj)
         elif id:
-             return ContentType.objects.get_for_id(id, using=using)
+             return ContentType.objects.db_manager(using).get_for_id(id)
         else:
             # This should never happen. I love comments like this, don't you?
             raise Exception("Impossible arguments to GFK.get_content_type!")
             join_table = qn(self.field.m2m_db_table()),
             source_col_name = qn(self.field.m2m_column_name()),
             target_col_name = qn(self.field.m2m_reverse_name()),
-            content_type = ContentType.objects.get_for_model(instance, using=instance._state.db),
+            content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(instance),
             content_type_field_name = self.field.content_type_field_name,
             object_id_field_name = self.field.object_id_field_name
         )

File django/contrib/contenttypes/models.py

     # This cache is shared by all the get_for_* methods.
     _cache = {}
 
-    def get_by_natural_key(self, app_label, model, using=None):
-        db = using or DEFAULT_DB_ALIAS
+    def get_by_natural_key(self, app_label, model):
         try:
-            ct = self.__class__._cache[db][(app_label, model)]
+            ct = self.__class__._cache[self.db][(app_label, model)]
         except KeyError:
-            ct = self.using(db).get(app_label=app_label, model=model)
+            ct = self.get(app_label=app_label, model=model)
         return ct
 
-    def get_for_model(self, model, using=None):
+    def get_for_model(self, model):
         """
         Returns the ContentType object for a given model, creating the
         ContentType if necessary. Lookups are cached so that subsequent lookups
         for the same model don't hit the database.
         """
-        db = using or DEFAULT_DB_ALIAS
         opts = model._meta
         while opts.proxy:
             model = opts.proxy_for_model
             opts = model._meta
         key = (opts.app_label, opts.object_name.lower())
         try:
-            ct = self.__class__._cache[db][key]
+            ct = self.__class__._cache[self.db][key]
         except KeyError:
             # Load or create the ContentType entry. The smart_unicode() is
             # needed around opts.verbose_name_raw because name_raw might be a
             # django.utils.functional.__proxy__ object.
-            ct, created = self.using(db).get_or_create(
+            ct, created = self.get_or_create(
                 app_label = opts.app_label,
                 model = opts.object_name.lower(),
                 defaults = {'name': smart_unicode(opts.verbose_name_raw)},
             )
-            self._add_to_cache(db, ct)
+            self._add_to_cache(self.db, ct)
 
         return ct
 
-    def get_for_id(self, id, using=None):
+    def get_for_id(self, id):
         """
         Lookup a ContentType by ID. Uses the same shared cache as get_for_model
         (though ContentTypes are obviously not created on-the-fly by get_by_id).
         """
-        db = using or DEFAULT_DB_ALIAS
         try:
-            ct = self.__class__._cache[db][id]
-
+            ct = self.__class__._cache[self.db][id]
         except KeyError:
             # This could raise a DoesNotExist; that's correct behavior and will
             # make sure that only correct ctypes get stored in the cache dict.
-            ct = self.using(db).get(pk=id)
-            self._add_to_cache(db, ct)
+            ct = self.get(pk=id)
+            self._add_to_cache(self.db, ct)
         return ct
 
     def clear_cache(self):

File django/core/serializers/python.py

                 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
                     def m2m_convert(value):
                         if hasattr(value, '__iter__'):
-                            return field.rel.to._default_manager.get_by_natural_key(*value, **{'using':db}).pk
+                            return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk
                         else:
                             return smart_unicode(field.rel.to._meta.pk.to_python(value))
                 else:
                 if field_value is not None:
                     if hasattr(field.rel.to._default_manager, 'get_by_natural_key'):
                         if hasattr(field_value, '__iter__'):
-                            obj = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':db})
+                            obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value)
                             value = getattr(obj, field.rel.field_name)
                         else:
                             value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)

File django/core/serializers/pyyaml.py

 """
 
 from StringIO import StringIO
+import decimal
 import yaml
 
-try:
-    import decimal
-except ImportError:
-    from django.utils import _decimal as decimal # Python 2.3 fallback
-
 from django.db import models
 from django.core.serializers.python import Serializer as PythonSerializer
 from django.core.serializers.python import Deserializer as PythonDeserializer

File django/core/serializers/xml_serializer.py

                 if keys:
                     # If there are 'natural' subelements, it must be a natural key
                     field_value = [getInnerText(k).strip() for k in keys]
-                    obj = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':self.db})
+                    obj = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value)
                     obj_pk = getattr(obj, field.rel.field_name)
                 else:
                     # Otherwise, treat like a normal PK
                 if keys:
                     # If there are 'natural' subelements, it must be a natural key
                     field_value = [getInnerText(k).strip() for k in keys]
-                    obj_pk = field.rel.to._default_manager.get_by_natural_key(*field_value, **{'using':self.db}).pk
+                    obj_pk = field.rel.to._default_manager.db_manager(self.db).get_by_natural_key(*field_value).pk
                 else:
                     # Otherwise, treat like a normal PK value.
                     obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk'))

File django/db/models/manager.py

-import django.utils.copycompat as copy
+from django.utils import copycompat as copy
+
+from django.db import DEFAULT_DB_ALIAS
 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
 from django.db.models import signals
 from django.db.models.fields import FieldDoesNotExist
         self._set_creation_counter()
         self.model = None
         self._inherited = False
+        self._db = None
 
     def contribute_to_class(self, model, name):
         # TODO: Use weakref because of possible memory leak / circular reference.
         mgr._inherited = True
         return mgr
 
+    def db_manager(self, alias):
+        obj = copy.copy(self)
+        obj._db = alias
+        return obj
+    
+    @property
+    def db(self):
+        return self._db or DEFAULT_DB_ALIAS
+
     #######################
     # PROXIES TO QUERYSET #
     #######################
         """Returns a new QuerySet object.  Subclasses can override this method
         to easily customize the behavior of the Manager.
         """
-        return QuerySet(self.model)
+        qs = QuerySet(self.model)
+        if self._db is not None:
+            qs = qs.using(self._db)
+        return qs
 
     def none(self):
         return self.get_empty_query_set()
 
     def using(self, *args, **kwargs):
         return self.get_query_set().using(*args, **kwargs)
-
+    
     def exists(self, *args, **kwargs):
         return self.get_query_set().exists(*args, **kwargs)
 

File docs/topics/db/multi-db.txt

 
     >>> user_obj.save(using='new_users')
     >>> user_obj.delete(using='legacy_users')
+
+
+Using ``Managers`` with Multiple Databases
+==========================================
+
+When you call ``using()`` Django returns a ``QuerySet`` that will be evaluated
+against that database.  However, sometimes you want to chain ``using()``
+together with a cusotm manager method that doesn't return a ``QuerySet``,
+such as the ``get_by_natural_key`` method.  To solve this issue you can use the
+``db_manager()`` method on a manager.  This method returns a copy of the
+*manager* bound to that specific database.  This let's you do things like::
+    
+    >>> Book.objects.db("other").get_by_natural_key(...)
+
+If you are overiding ``get_query_set()`` on your manager you must be sure to
+either, a) call the method on the parent (using ``super()``), or b) do the
+appropriate handling of the ``_db`` attribute on the manager.  For example if
+you wanted to return a custom ``QuerySet`` class from the ``get_query_set``
+method you could do this::
+
+    class MyManager(models.Manager):
+        ...
+        def get_query_set(self):
+            qs = CustomQuerySet(self.model)
+            if self._db is not None:
+                qs = qs.using(self._db)
+            return qs

File tests/modeltests/fixtures/models.py

                                          self.tagged, self.name)
 
 class PersonManager(models.Manager):
-    def get_by_natural_key(self, name, using=None):
-        return self.using(using).get(name=name)
+    def get_by_natural_key(self, name):
+        return self.get(name=name)
 
 class Person(models.Model):
     objects = PersonManager()

File tests/regressiontests/fixtures_regress/models.py

 # Check for forward references in FKs and M2Ms with natural keys
 
 class TestManager(models.Manager):
-    def get_by_natural_key(self, key, using=None):
-        return self.using(using).get(name=key)
+    def get_by_natural_key(self, key):
+        return self.get(name=key)
 
 class Store(models.Model):
     objects = TestManager()

File tests/regressiontests/multiple_database/models.py

         ordering = ('source',)
 
 class PersonManager(models.Manager):
-    def get_by_natural_key(self, name, using=None):
-        return self.using(using).get(name=name)
+    def get_by_natural_key(self, name):
+        return self.get(name=name)
 
 class Person(models.Model):
     objects = PersonManager()

File tests/regressiontests/multiple_database/tests.py

     def test_user_profiles(self):
 
         alice = User.objects.create_user('alice', 'alice@example.com')
-        bob = User.objects.create_user('bob', 'bob@example.com', using='other')
+        bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com')
 
         alice_profile = UserProfile(user=alice, flavor='chocolate')
         alice_profile.save()