Commits

Gregor Müllegger committed 895f4d0

Determine M2M fields to create intermediate table from through model instead
of assuming that they are given constants. This allows custom M2M subclasses
to define new fields for the through model without marking the through model
explicitly as auto_created=False.

Comments (0)

Files changed (1)

south/creator/actions.py

 import sys
 import datetime
 
-from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
+from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT, ManyToManyField
 from django.db.models.fields import FieldDoesNotExist, NOT_PROVIDED, CharField, TextField
+from django.utils.datastructures import SortedDict
 
 from south import modelsinspector
 from south.creator.freezer import remove_useless_attributes, model_key
     FORWARDS_TEMPLATE = '''
         # Adding M2M table for field %(field_name)s on '%(model_name)s'
         db.create_table(%(table_name)r, (
-            ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
-            (%(left_field)r, models.ForeignKey(orm[%(left_model_key)r], null=False)),
-            (%(right_field)r, models.ForeignKey(orm[%(right_model_key)r], null=False))
+            %(field_defs)s
         ))
         db.create_unique(%(table_name)r, [%(left_column)r, %(right_column)r])'''
     
             self.model._meta.app_label, 
             self.model._meta.object_name,
         )
-    
+
+    def split_model_def(self, model, model_def):
+        """
+        Given a model and its model def (a dict of field: triple), returns three
+        items: the real fields dict, the Meta dict, and the M2M fields dict.
+        """
+        real_fields = SortedDict()
+        meta = SortedDict()
+        m2m_fields = SortedDict()
+        for name, triple in model_def.items():
+            if name == "Meta":
+                meta = triple
+            elif isinstance(model._meta.get_field_by_name(name)[0], ManyToManyField):
+                m2m_fields[name] = triple
+            else:
+                real_fields[name] = triple
+        return real_fields, meta, m2m_fields
+
     def forwards_code(self):
-        
+        from south.creator.freezer import prep_for_freeze
+
+        model_def = prep_for_freeze(self.field.rel.through)
+        fields, meta, m2ms = self.split_model_def(self.field.rel.through, model_def)
+        field_defs = ",\n            ".join([
+            "(%r, %s)" % (name, defn) for name, defn
+            in self.triples_to_defs(fields).items()
+        ]) + ","
+
         return self.FORWARDS_TEMPLATE % {
             "model_name": self.model._meta.object_name,
             "field_name": self.field.name,
             "right_field": self.field.m2m_reverse_name()[:-3], # Remove the _id part
             "right_column": self.field.m2m_reverse_name(),
             "right_model_key": model_key(self.field.rel.to),
+            "field_defs": field_defs,
         }
 
     def backwards_code(self):