Commits

Anonymous committed 11af742

Respect models and model objects passed to the field when
collection field dependencies.
---
south/creator/freezer.py | 49 ++++++++++++++++++++++++++--------------
south/tests/__init__.py | 1 +
south/tests/fakeapp/models.py | 36 +++++++++++++++++++++++++++++-
south/tests/freezer.py | 15 ++++++++++++
4 files changed, 83 insertions(+), 18 deletions(-)
create mode 100644 south/tests/freezer.py

Comments (0)

Files changed (4)

south/creator/freezer.py

 import sys
 
 from django.db import models
+from django.db.models.base import ModelBase, Model
 from django.contrib.contenttypes.generic import GenericRelation
 
 from south.orm import FakeORM
-from south.utils import auto_model
+from south.utils import get_attribute, auto_through
 from south import modelsinspector
 
 def freeze_apps(apps):
     checked_models = checked_models or set()
     # Get deps for each field
     for field in model._meta.fields + model._meta.many_to_many:
-        depends.update(field_dependencies(field))
+        depends.update(field_dependencies(field, checked_models))
     # Add in any non-abstract bases
     for base in model.__bases__:
         if issubclass(base, models.Model) and hasattr(base, '_meta') and not base._meta.abstract:
 def field_dependencies(field, checked_models=None):
     checked_models = checked_models or set()
     depends = set()
-    if isinstance(field, (models.OneToOneField, models.ForeignKey, models.ManyToManyField, GenericRelation)):
-        if field.rel.to in checked_models:
-            return depends
-        checked_models.add(field.rel.to)
-        depends.add(field.rel.to)
-        depends.update(field_dependencies(field.rel.to._meta.pk, checked_models))
-        # Also include M2M throughs
-        if isinstance(field, models.ManyToManyField):
-            if field.rel.through:
-                if hasattr(field.rel, "through_model"): # 1.1 and below
-                    depends.add(field.rel.through_model)
-                else:
-                    # Make sure it's not an automatic one
-                    if not auto_model(field.rel.through):
-                        depends.add(field.rel.through) # 1.2 and up
+    arg_defs, kwarg_defs = modelsinspector.matching_details(field)
+    for attrname, options in arg_defs + kwarg_defs.values():
+        if options.get("ignore_if_auto_through", False) and auto_through(field):
+            continue
+        if options.get("is_value", False):
+            value = attrname
+        elif attrname == 'rel.through' and hasattr(getattr(field, 'rel', None), 'through_model'):
+            # Hack for django 1.1 and below, where the through model is stored
+            # in rel.through_model while rel.through stores only the model name.
+            value = field.rel.through_model
+        else:
+            try:
+                value = get_attribute(field, attrname)
+            except AttributeError:
+                if options.get("ignore_missing", False):
+                    continue
+                raise
+        if isinstance(value, Model):
+            value = value.__class__
+        if not isinstance(value, ModelBase):
+            continue
+        if getattr(value._meta, "proxy", False):
+            value = value._meta.proxy_for_model
+        if value in checked_models:
+            continue
+        checked_models.add(value)
+        depends.add(value)
+        depends.update(model_dependencies(value, checked_models))
+
     return depends
 
 ### Prettyprinters

south/tests/__init__.py

     from south.tests.autodetection import *
     from south.tests.logger import *
     from south.tests.inspector import *
+    from south.tests.freezer import *

south/tests/fakeapp/models.py

 from django.db import models
 from django.contrib.auth.models import User as UserAlias
 
+from south.modelsinspector import add_introspection_rules
+
 def default_func():
     return "yays"
 
 # Special case.
 class Other2(models.Model):
     # Try loading a field without a newline after it (inspect hates this)
-    close_but_no_cigar = models.PositiveIntegerField(primary_key=True)
+    close_but_no_cigar = models.PositiveIntegerField(primary_key=True)
+
+class CustomField(models.IntegerField):
+    def __init__(self, an_other_model, **kwargs):
+        super(CustomField, self).__init__(**kwargs)
+        self.an_other_model = an_other_model
+
+add_introspection_rules([
+    (
+        [CustomField],
+        [],
+        {'an_other_model': ('an_other_model', {})},
+    ),
+], ['^south\.tests\.fakeapp\.models\.CustomField'])
+
+class BaseModel(models.Model):
+    pass
+
+class SubModel(BaseModel):
+    others = models.ManyToManyField(Other1)
+    custom = CustomField(Other2)
+
+class CircularA(models.Model):
+    c = models.ForeignKey('CircularC')
+
+class CircularB(models.Model):
+    a = models.ForeignKey(CircularA)
+
+class CircularC(models.Model):
+    b = models.ForeignKey(CircularB)
+
+class Recursive(models.Model):
+   self = models.ForeignKey('self')

south/tests/freezer.py

+import unittest
+
+from south.creator.freezer import model_dependencies
+from south.tests.fakeapp import models
+
+class TestFreezer(unittest.TestCase):
+    def test_dependencies(self):
+        self.assertEqual(set(model_dependencies(models.SubModel)),
+                         set([models.BaseModel, models.Other1, models.Other2]))
+
+        self.assertEqual(set(model_dependencies(models.CircularA)),
+                         set([models.CircularA, models.CircularB, models.CircularC]))
+
+        self.assertEqual(set(model_dependencies(models.Recursive)),
+                         set([models.Recursive]))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.