Tim Dawborn avatar Tim Dawborn committed 0e37f5a

Added support for index_together meta option. See #1217.

Comments (0)

Files changed (4)

docs/autodetector.rst

 constraints).
 
 
+Multicolumn index changes
+^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Django 1.5 introduced ``index_together`` in a model's Meta, allowing you to
+specify multicolumn indexes. South will detect and change the indexes on the
+database accordingly.

docs/ormfreezing.rst

  db_table
  db_tablespace
  unique_together
+ index_together
  ordering
 
 If there's something else you think should be frozen in the Meta, but which
-isn't, file a bug and we'll look into it.
+isn't, file a bug and we'll look into it.

south/creator/changes.py

                     params['model']._meta.object_name.lower(),
                     "_".join([x.name for x in params['fields']]),
                 ))
+            elif change_name == "AddIndex":
+                parts.append("add_index_%s_%s" % (
+                    params['model']._meta.object_name.lower(),
+                    "_".join([x.name for x in params['fields']]),
+                ))
+            elif change_name == "DeleteIndex":
+                parts.append("del_index_%s_%s" % (
+                    params['model']._meta.object_name.lower(),
+                    "_".join([x.name for x in params['fields']]),
+                ))
         return ("__".join(parts))[:70]
     
     def get_changes(self):
                         field = self.old_orm[key + ":" + fieldname]
                         if auto_through(field):
                             yield ("DeleteM2M", {"model": self.old_orm[key], "field": field})
-                    # And any unique constraints it had 
-                    unique_together = eval(old_meta.get("unique_together", "[]"))
-                    if unique_together:
-                        # If it's only a single tuple, make it into the longer one
-                        if isinstance(unique_together[0], string_types):
-                            unique_together = [unique_together]
-                        # For each combination, make an action for it
-                        for fields in unique_together:
-                            yield ("DeleteUnique", {
-                                "model": self.old_orm[key],
-                                "fields": [self.old_orm[key]._meta.get_field_by_name(x)[0] for x in fields],
-                            })
+                    # And any index/uniqueness constraints it had
+                    for attr, operation in (("unique_together", "DeleteUnique"), ("index_together", "DeleteIndex")):
+                        together = eval(old_meta.get(attr, "[]"))
+                        if together:
+                            # If it's only a single tuple, make it into the longer one
+                            if isinstance(together[0], string_types):
+                                together = [together]
+                            # For each combination, make an action for it
+                            for fields in together:
+                                yield (operation, {
+                                    "model": self.old_orm[key],
+                                    "fields": [self.old_orm[key]._meta.get_field_by_name(x)[0] for x in fields],
+                                })
                 # We always add it in here so we ignore it later
                 deleted_models.add(key)
         
                         field = self.current_field_from_key(key, fieldname)
                         if auto_through(field):
                             yield ("AddM2M", {"model": self.current_model_from_key(key), "field": field})
-                    # And any unique constraints it has 
-                    unique_together = eval(new_meta.get("unique_together", "[]"))
-                    if unique_together:
-                        # If it's only a single tuple, make it into the longer one
-                        if isinstance(unique_together[0], string_types):
-                            unique_together = [unique_together]
-                        # For each combination, make an action for it
-                        for fields in unique_together:
-                            yield ("AddUnique", {
-                                "model": self.current_model_from_key(key),
-                                "fields": [self.current_model_from_key(key)._meta.get_field_by_name(x)[0] for x in fields],
-                            })
+                    # And any index/uniqueness constraints it has
+                    for attr, operation in (("unique_together", "AddUnique"), ("index_together", "AddIndex")):
+                        together = eval(new_meta.get(attr, "[]"))
+                        if together:
+                            # If it's only a single tuple, make it into the longer one
+                            if isinstance(together[0], string_types):
+                                together = [together]
+                            # For each combination, make an action for it
+                            for fields in together:
+                                yield (operation, {
+                                    "model": self.current_model_from_key(key),
+                                    "fields": [self.current_model_from_key(key)._meta.get_field_by_name(x)[0] for x in fields],
+                                })
         
         # Now, for every model that's stayed the same, check its fields.
         for key in self.old_defs:
                     if not auto_through(old_field) and auto_through(new_field):
                         yield ("AddM2M", {"model": self.current_model_from_key(key), "field": new_field})
                 
-                ## See if the unique_togethers have changed
-                # First, normalise them into lists of sets.
-                old_unique_together = eval(old_meta.get("unique_together", "[]"))
-                new_unique_together = eval(new_meta.get("unique_together", "[]"))
-                if old_unique_together and isinstance(old_unique_together[0], string_types):
-                    old_unique_together = [old_unique_together]
-                if new_unique_together and isinstance(new_unique_together[0], string_types):
-                    new_unique_together = [new_unique_together]
-                old_unique_together = list(map(set, old_unique_together))
-                new_unique_together = list(map(set, new_unique_together))
-                # See if any appeared or disappeared
-                for item in old_unique_together:
-                    if item not in new_unique_together:
-                        yield ("DeleteUnique", {
-                            "model": self.old_orm[key],
-                            "fields": [self.old_orm[key + ":" + x] for x in item],
-                        })
-                for item in new_unique_together:
-                    if item not in old_unique_together:
-                        yield ("AddUnique", {
-                            "model": self.current_model_from_key(key),
-                            "fields": [self.current_field_from_key(key, x) for x in item],
-                        })
+                ## See if the {index,unique}_togethers have changed
+                for attr, add_operation, del_operation in (("unique_together", "AddUnique", "DeleteUnique"), ("index_together", "AddIndex", "DeleteIndex")):
+                    # First, normalise them into lists of sets.
+                    old_together = eval(old_meta.get(attr, "[]"))
+                    new_together = eval(new_meta.get(attr, "[]"))
+                    if old_together and isinstance(old_together[0], string_types):
+                        old_together = [old_together]
+                    if new_together and isinstance(new_together[0], string_types):
+                        new_together = [new_together]
+                    old_together = list(map(set, old_together))
+                    new_together = list(map(set, new_together))
+                    # See if any appeared or disappeared
+                    for item in old_together:
+                        if item not in new_together:
+                            yield (del_operation, {
+                                "model": self.old_orm[key],
+                                "fields": [self.old_orm[key + ":" + x] for x in item],
+                            })
+                    for item in new_together:
+                        if item not in old_together:
+                            yield (add_operation, {
+                                "model": self.current_model_from_key(key),
+                                "fields": [self.current_field_from_key(key, x) for x in item],
+                            })
 
     @classmethod
     def is_triple(cls, triple):
                 "model_def": real_fields,
             })
             
-            # Then, add any uniqueness that's around
+            # Then, add any indexing/uniqueness that's around
             if meta:
-                unique_together = eval(meta.get("unique_together", "[]"))
-                if unique_together:
-                    # If it's only a single tuple, make it into the longer one
-                    if isinstance(unique_together[0], string_types):
-                        unique_together = [unique_together]
-                    # For each combination, make an action for it
-                    for fields in unique_together:
-                        yield ("AddUnique", {
-                            "model": model,
-                            "fields": [model._meta.get_field_by_name(x)[0] for x in fields],
-                        })
+                for attr, operation in (("unique_together", "AddUnique"), ("index_together", "AddIndex")):
+                    together = eval(meta.get(attr, "[]"))
+                    if together:
+                        # If it's only a single tuple, make it into the longer one
+                        if isinstance(together[0], string_types):
+                            together = [together]
+                        # For each combination, make an action for it
+                        for fields in together:
+                            yield (operation, {
+                                "model": model,
+                                "fields": [model._meta.get_field_by_name(x)[0] for x in fields],
+                            })
             
             # Finally, see if there's some M2M action
             for name, triple in m2m_fields.items():

south/modelsinspector.py

     "db_table": ["db_table", {"default_attr_concat": ["%s_%s", "app_label", "module_name"]}],
     "db_tablespace": ["db_tablespace", {"default": settings.DEFAULT_TABLESPACE}],
     "unique_together": ["unique_together", {"default": []}],
+    "index_together": ["index_together", {"default": []}],
     "ordering": ["ordering", {"default": []}],
     "proxy": ["proxy", {"default": False, "ignore_missing": True}],
 }
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.