Luke Plant committed a73e5b4

Fixed some problems with cyclic imports that occurred especially when trying to clear out the database

  • Participants
  • Parent commits f194238

Comments (0)

Files changed (3)

File django/tagging/

     def __init__(self, m2m_model=None, from_model=None, to_model=Any,
             from_attrname=None, to_attrname=None):
         self.from_model = from_model
-        self.from_ct = utils.get_content_type_id(from_model)
-        self.to_ct = (to_model == Any) and Any or utils.get_content_type_id(to_model)
         self.from_attrname = from_attrname
         self.to_attrname = to_attrname
         self.m2m_model = m2m_model
+        # Delay getting from_ct and to_ct, because that involves a DB lookup
+        # and can cause import errors due to cyclic imports
     def __get__(self, instance, instance_type=None):
         if instance is None:
             raise AttributeError, "Manager must be accessed via instance"
+        if not hasattr(self, 'from_ct'):
+            self.from_ct = utils.get_content_type_id(from_model)
+            self.to_ct = (to_model == Any) and Any or utils.get_content_type_id(to_model)
         # Dynamically create a class that subclasses m2m_model's manager
         desc = self # so we can access the descriptor inside the nested class
         superclass = self.m2m_model._default_manager.__class__
     added to the class.
     See examples below.
+    WARNING: this function can add some cyclic import dependencies
+    that can make it difficult to drop your database tables using
+    ' reset'
     if creator_model is Any and target_model is Any:
         raise Exception("At least one of creator_model and target_model must be set")

File django/tagging/

 class Tag(models.Model):
     text = models.CharField("Text", maxlength=32)
     target_id = models.CharField("'Target' ID", maxlength=64) # 64 should be enough for anyone...
-    target_ct = models.ForeignKey(ContentType, verbose_name="'Target' content type")
+    target_ct = models.ForeignKey(ContentType, verbose_name="'Target' content type", 
+        related_name='tag_target_set')
     creator_id = models.CharField("'creator' ID", maxlength=64)
-    creator_ct = models.ForeignKey(ContentType, verbose_name="'creator' content type")
+    creator_ct = models.ForeignKey(ContentType, verbose_name="'creator' content type",
+        related_name='tag_creator_set')
     added = models.DateTimeField("Date added", auto_now_add=True)
     target = GenericForeignKey('target_id', 'target_ct_id')

File django/tagging/

 def pk_from_str(pk_str, content_type_id):
     """Get a primary key value of the correct type."""
-        mapper = _pk_from_str_mappers[content_type_id]
-    except:
+        mapper = _pk_from_str_mappers[get_model(content_type_id)]
+    except KeyError:
         raise Exception("No string-to-primary key mapper has been configured for content type %s" % content_type_id)
     return mapper(pk_str)
 def pk_to_str(pk, content_type_id):
     """Get a string representation of a primary key value."""
-        mapper = _pk_to_str_mappers[content_type_id]
+        mapper = _pk_to_str_mappers[get_model(content_type_id)]
         raise Exception("No primay key-to-string mapper has been configured for content type %s" % content_type_id)
     return mapper(pk)
 def register_mappers(model, pk_to_str=None, pk_from_str=None):
     """Register mapper functions for converting primary key values to and
     from strings, for a given model."""
-    content_type_id = get_content_type_id(model)
+    # register_mappers is called from a model module file
+    # typically, so looking up ContentType ids immediately
+    #  can cause have havoc when trying to *uninstall* models.
+    # So we key on 'model', not content type id
     if pk_to_str is not None:
-        _pk_to_str_mappers[content_type_id] = pk_to_str
+        _pk_to_str_mappers[model] = pk_to_str
     if pk_from_str is not None:
-        _pk_from_str_mappers[content_type_id] = pk_from_str
+        _pk_from_str_mappers[model] = pk_from_str
 def register_renderer(model, renderer):
     """Registers a function to be used to implement the render method
     of Tag when it's target is the supplied model type.
     The function is passed the tag object, and must return
     the html to be displayed on a page."""
-    _renderers[get_content_type_id(model)] = renderer
+    _renderers[model] = renderer
 def get_renderer(model_ct):
     """Gets the renderer for a given content type id, 
     or None if none can be found."""
-    return _renderers.get(model_ct, None)
+    return _renderers.get(get_model(model_ct), None)
 def tagging_normaliser(tag_sum, collection):
     """Example 'normaliser' for calculating the 'weight'