Exemplo n.º 1
0
    def get_changes(self):
        """
        Returns the difference between the old and new sets of models as a 5-tuple:
        added_models, deleted_models, added_fields, deleted_fields, changed_fields
        """

        deleted_models = set()

        # See if anything's vanished
        for key in self.old_defs:
            if key not in self.new_defs:
                # We shouldn't delete it if it was managed=False
                old_fields, old_meta, old_m2ms = self.split_model_def(
                    self.old_orm[key], self.old_defs[key])
                if old_meta.get("managed", "True") != "False":
                    # Alright, delete it.
                    yield ("DeleteModel", {
                        "model": self.old_orm[key],
                        "model_def": old_fields,
                    })
                    # Also make sure we delete any M2Ms it had.
                    for fieldname in old_m2ms:
                        # Only delete its stuff if it wasn't a through=.
                        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], basestring):
                            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
                                ],
                            })
                # We always add it in here so we ignore it later
                deleted_models.add(key)

        # Or appeared
        for key in self.new_defs:
            if key not in self.old_defs:
                # We shouldn't add it if it's managed=False
                new_fields, new_meta, new_m2ms = self.split_model_def(
                    self.current_model_from_key(key), self.new_defs[key])
                if new_meta.get("managed", "True") != "False":
                    yield ("AddModel", {
                        "model": self.current_model_from_key(key),
                        "model_def": new_fields,
                    })
                    # Also make sure we add any M2Ms it has.
                    for fieldname in new_m2ms:
                        # Only create its stuff if it wasn't a through=.
                        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], basestring):
                            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
                                ],
                            })

        # Now, for every model that's stayed the same, check its fields.
        for key in self.old_defs:
            if key not in deleted_models:

                old_fields, old_meta, old_m2ms = self.split_model_def(
                    self.old_orm[key], self.old_defs[key])
                new_fields, new_meta, new_m2ms = self.split_model_def(
                    self.current_model_from_key(key), self.new_defs[key])

                # Find fields that have vanished.
                for fieldname in old_fields:
                    if fieldname not in new_fields:
                        # Don't do it for any fields we're ignoring
                        field = self.old_orm[key + ":" + fieldname]
                        field_allowed = True
                        for field_type in self.IGNORED_FIELD_TYPES:
                            if isinstance(field, field_type):
                                field_allowed = False
                        if field_allowed:
                            # Looks alright.
                            yield ("DeleteField", {
                                "model": self.old_orm[key],
                                "field": field,
                                "field_def": old_fields[fieldname],
                            })

                # And ones that have appeared
                for fieldname in new_fields:
                    if fieldname not in old_fields:
                        # Don't do it for any fields we're ignoring
                        field = self.current_field_from_key(key, fieldname)
                        field_allowed = True
                        for field_type in self.IGNORED_FIELD_TYPES:
                            if isinstance(field, field_type):
                                field_allowed = False
                        if field_allowed:
                            # Looks alright.
                            yield ("AddField", {
                                "model": self.current_model_from_key(key),
                                "field": field,
                                "field_def": new_fields[fieldname],
                            })

                # Find M2Ms that have vanished
                for fieldname in old_m2ms:
                    if fieldname not in new_m2ms:
                        # Only delete its stuff if it wasn't a through=.
                        field = self.old_orm[key + ":" + fieldname]
                        if auto_through(field):
                            yield ("DeleteM2M", {
                                "model": self.old_orm[key],
                                "field": field
                            })

                # Find M2Ms that have appeared
                for fieldname in new_m2ms:
                    if fieldname not in old_m2ms:
                        # Only create its stuff if it wasn't a through=.
                        field = self.current_field_from_key(key, fieldname)
                        if auto_through(field):
                            yield ("AddM2M", {
                                "model": self.current_model_from_key(key),
                                "field": field
                            })

                # For the ones that exist in both models, see if they were changed
                for fieldname in set(old_fields).intersection(set(new_fields)):
                    # Non-index changes
                    if self.different_attributes(
                            remove_useless_attributes(old_fields[fieldname],
                                                      True, True),
                            remove_useless_attributes(new_fields[fieldname],
                                                      True, True)):
                        yield ("ChangeField", {
                            "model":
                            self.current_model_from_key(key),
                            "old_field":
                            self.old_orm[key + ":" + fieldname],
                            "new_field":
                            self.current_field_from_key(key, fieldname),
                            "old_def":
                            old_fields[fieldname],
                            "new_def":
                            new_fields[fieldname],
                        })
                    # Index changes
                    old_field = self.old_orm[key + ":" + fieldname]
                    new_field = self.current_field_from_key(key, fieldname)
                    if not old_field.db_index and new_field.db_index:
                        # They've added an index.
                        yield ("AddIndex", {
                            "model": self.current_model_from_key(key),
                            "fields": [new_field],
                        })
                    if old_field.db_index and not new_field.db_index:
                        # They've removed an index.
                        yield ("DeleteIndex", {
                            "model": self.old_orm[key],
                            "fields": [old_field],
                        })
                    # See if their uniques have changed
                    if old_field.unique != new_field.unique:
                        # Make sure we look at the one explicitly given to see what happened
                        if new_field.unique:
                            yield ("AddUnique", {
                                "model": self.current_model_from_key(key),
                                "fields": [new_field],
                            })
                        else:
                            yield ("DeleteUnique", {
                                "model": self.old_orm[key],
                                "fields": [old_field],
                            })

                # See if there's any M2Ms that have changed.
                for fieldname in set(old_m2ms).intersection(set(new_m2ms)):
                    old_field = self.old_orm[key + ":" + fieldname]
                    new_field = self.current_field_from_key(key, fieldname)
                    # Have they _added_ a through= ?
                    if auto_through(old_field) and not auto_through(new_field):
                        yield ("DeleteM2M", {
                            "model": self.old_orm[key],
                            "field": old_field
                        })
                    # Have they _removed_ a through= ?
                    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],
                                                      basestring):
                    old_unique_together = [old_unique_together]
                if new_unique_together and isinstance(new_unique_together[0],
                                                      basestring):
                    new_unique_together = [new_unique_together]
                old_unique_together = map(set, old_unique_together)
                new_unique_together = 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
                            ],
                        })
Exemplo n.º 2
0
    def get_changes(self):
        """
        Returns the difference between the old and new sets of models as a 5-tuple:
        added_models, deleted_models, added_fields, deleted_fields, changed_fields
        """

        deleted_models = set()

        # See if anything's vanished
        for key in self.old_defs:
            if key not in self.new_defs:
                # We shouldn't delete it if it was managed=False
                if self.old_defs[key].get("Meta", {}).get("managed", "True") != "False":
                    # Alright, delete it.
                    yield (
                        "DeleteModel",
                        {
                            "model": self.old_orm[key],
                            "model_def": self.split_model_def(self.old_orm[key], self.old_defs[key])[0],
                        },
                    )
                # We always add it in here so we ignore it later
                deleted_models.add(key)

        # Or appeared
        for key in self.new_defs:
            if key not in self.old_defs:
                # We shouldn't add it if it's managed=False
                if self.new_defs[key].get("Meta", {}).get("managed", "True") != "False":
                    yield (
                        "AddModel",
                        {
                            "model": self.current_model_from_key(key),
                            "model_def": self.split_model_def(self.current_model_from_key(key), self.new_defs[key])[0],
                        },
                    )

        # Now, for every model that's stayed the same, check its fields.
        for key in self.old_defs:
            if key not in deleted_models:

                still_there = set()

                old_fields, old_meta, old_m2ms = self.split_model_def(self.old_orm[key], self.old_defs[key])
                new_fields, new_meta, new_m2ms = self.split_model_def(
                    self.current_model_from_key(key), self.new_defs[key]
                )

                # Find fields that have vanished.
                for fieldname in old_fields:
                    if fieldname not in new_fields:
                        yield (
                            "DeleteField",
                            {
                                "model": self.old_orm[key],
                                "field": self.old_orm[key + ":" + fieldname],
                                "field_def": old_fields[fieldname],
                            },
                        )

                # And ones that have appeared
                for fieldname in new_fields:
                    if fieldname not in old_fields:
                        yield (
                            "AddField",
                            {
                                "model": self.current_model_from_key(key),
                                "field": self.current_field_from_key(key, fieldname),
                                "field_def": new_fields[fieldname],
                            },
                        )

                # Find M2Ms that have vanished
                for fieldname in old_m2ms:
                    if fieldname not in new_m2ms:
                        # Only delete its stuff if it wasn't a through=.
                        field = self.old_orm[key + ":" + fieldname]
                        if auto_through(field):
                            yield ("DeleteM2M", {"model": self.old_orm[key], "field": field})

                # Find M2Ms that have appeared
                for fieldname in new_m2ms:
                    if fieldname not in old_m2ms:
                        # Only create its stuff if it wasn't a through=.
                        field = self.current_field_from_key(key, fieldname)
                        if auto_through(field):
                            yield ("AddM2M", {"model": self.current_model_from_key(key), "field": field})

                # For the ones that exist in both models, see if they were changed
                for fieldname in set(old_fields).intersection(set(new_fields)):
                    if self.different_attributes(
                        remove_useless_attributes(old_fields[fieldname], True),
                        remove_useless_attributes(new_fields[fieldname], True),
                    ):
                        yield (
                            "ChangeField",
                            {
                                "model": self.current_model_from_key(key),
                                "old_field": self.old_orm[key + ":" + fieldname],
                                "new_field": self.current_field_from_key(key, fieldname),
                                "old_def": old_fields[fieldname],
                                "new_def": new_fields[fieldname],
                            },
                        )
                    # See if their uniques have changed
                    old_field = self.old_orm[key + ":" + fieldname]
                    new_field = self.current_field_from_key(key, fieldname)
                    if old_field.unique != new_field.unique:
                        # Make sure we look at the one explicitly given to see what happened
                        if new_field.unique:
                            yield (
                                "AddUnique",
                                {
                                    "model": self.current_model_from_key(key),
                                    "fields": [self.current_field_from_key(key, fieldname)],
                                },
                            )
                        else:
                            yield (
                                "DeleteUnique",
                                {"model": self.old_orm[key], "fields": [self.old_orm[key + ":" + fieldname]]},
                            )

                # See if there's any M2Ms that have changed.
                for fieldname in set(old_m2ms).intersection(set(new_m2ms)):
                    old_field = self.old_orm[key + ":" + fieldname]
                    new_field = self.current_field_from_key(key, fieldname)
                    # Have they _added_ a through= ?
                    if auto_through(old_field) and not auto_through(new_field):
                        yield ("DeleteM2M", {"model": self.old_orm[key], "field": old_field})
                    # Have they _removed_ a through= ?
                    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], basestring):
                    old_unique_together = [old_unique_together]
                if new_unique_together and isinstance(new_unique_together[0], basestring):
                    new_unique_together = [new_unique_together]
                old_unique_together = map(set, old_unique_together)
                new_unique_together = 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],
                            },
                        )
Exemplo n.º 3
0
 def get_changes(self):
     """
     Returns the difference between the old and new sets of models as a 5-tuple:
     added_models, deleted_models, added_fields, deleted_fields, changed_fields
     """
     
     deleted_models = set()
     
     # See if anything's vanished
     for key in self.old_defs:
         if key not in self.new_defs:
             # We shouldn't delete it if it was managed=False
             old_fields, old_meta, old_m2ms = self.split_model_def(self.old_orm[key], self.old_defs[key])
             if old_meta.get("managed", "True") != "False":
                 # Alright, delete it.
                 yield ("DeleteModel", {
                     "model": self.old_orm[key], 
                     "model_def": old_fields,
                 })
                 # Also make sure we delete any M2Ms it had.
                 for fieldname in old_m2ms:
                     # Only delete its stuff if it wasn't a through=.
                     field = self.old_orm[key + ":" + fieldname]
                     if auto_through(field):
                         yield ("DeleteM2M", {"model": self.old_orm[key], "field": field})
                 # 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)
     
     # Or appeared
     for key in self.new_defs:
         if key not in self.old_defs:
             # We shouldn't add it if it's managed=False
             new_fields, new_meta, new_m2ms = self.split_model_def(self.current_model_from_key(key), self.new_defs[key])
             if new_meta.get("managed", "True") != "False":
                 yield ("AddModel", {
                     "model": self.current_model_from_key(key), 
                     "model_def": new_fields,
                 })
                 # Also make sure we add any M2Ms it has.
                 for fieldname in new_m2ms:
                     # Only create its stuff if it wasn't a through=.
                     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 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 key not in deleted_models:
             
             old_fields, old_meta, old_m2ms = self.split_model_def(self.old_orm[key], self.old_defs[key])
             new_fields, new_meta, new_m2ms = self.split_model_def(self.current_model_from_key(key), self.new_defs[key])
             
             # Do nothing for models which are now not managed.
             if new_meta.get("managed", "True") == "False":
                 continue
             
             # Find fields that have vanished.
             for fieldname in old_fields:
                 if fieldname not in new_fields:
                     # Don't do it for any fields we're ignoring
                     field = self.old_orm[key + ":" + fieldname]
                     field_allowed = True
                     for field_type in self.IGNORED_FIELD_TYPES:
                         if isinstance(field, field_type):
                             field_allowed = False
                     if field_allowed:
                         # Looks alright.
                         yield ("DeleteField", {
                             "model": self.old_orm[key],
                             "field": field,
                             "field_def": old_fields[fieldname],
                         })
             
             # And ones that have appeared
             for fieldname in new_fields:
                 if fieldname not in old_fields:
                     # Don't do it for any fields we're ignoring
                     field = self.current_field_from_key(key, fieldname)
                     field_allowed = True
                     for field_type in self.IGNORED_FIELD_TYPES:
                         if isinstance(field, field_type):
                             field_allowed = False
                     if field_allowed:
                         # Looks alright.
                         yield ("AddField", {
                             "model": self.current_model_from_key(key),
                             "field": field,
                             "field_def": new_fields[fieldname],
                         })
             
             # Find M2Ms that have vanished
             for fieldname in old_m2ms:
                 if fieldname not in new_m2ms:
                     # Only delete its stuff if it wasn't a through=.
                     field = self.old_orm[key + ":" + fieldname]
                     if auto_through(field):
                         yield ("DeleteM2M", {"model": self.old_orm[key], "field": field})
             
             # Find M2Ms that have appeared
             for fieldname in new_m2ms:
                 if fieldname not in old_m2ms:
                     # Only create its stuff if it wasn't a through=.
                     field = self.current_field_from_key(key, fieldname)
                     if auto_through(field):
                         yield ("AddM2M", {"model": self.current_model_from_key(key), "field": field})
             
             # For the ones that exist in both models, see if they were changed
             for fieldname in set(old_fields).intersection(set(new_fields)):
                 # Non-index changes
                 if self.different_attributes(
                  remove_useless_attributes(old_fields[fieldname], True, True),
                  remove_useless_attributes(new_fields[fieldname], True, True)):
                     yield ("ChangeField", {
                         "model": self.current_model_from_key(key),
                         "old_field": self.old_orm[key + ":" + fieldname],
                         "new_field": self.current_field_from_key(key, fieldname),
                         "old_def": old_fields[fieldname],
                         "new_def": new_fields[fieldname],
                     })
                 # Index changes
                 old_field = self.old_orm[key + ":" + fieldname]
                 new_field = self.current_field_from_key(key, fieldname)
                 if not old_field.db_index and new_field.db_index:
                     # They've added an index.
                     yield ("AddIndex", {
                         "model": self.current_model_from_key(key),
                         "fields": [new_field],
                     })
                 if old_field.db_index and not new_field.db_index:
                     # They've removed an index.
                     yield ("DeleteIndex", {
                         "model": self.old_orm[key],
                         "fields": [old_field],
                     })
                 # See if their uniques have changed
                 if old_field.unique != new_field.unique:
                     # Make sure we look at the one explicitly given to see what happened
                     if new_field.unique:
                         yield ("AddUnique", {
                             "model": self.current_model_from_key(key),
                             "fields": [new_field],
                         })
                     else:
                         yield ("DeleteUnique", {
                             "model": self.old_orm[key],
                             "fields": [old_field],
                         })
             
             # See if there's any M2Ms that have changed.
             for fieldname in set(old_m2ms).intersection(set(new_m2ms)):
                 old_field = self.old_orm[key + ":" + fieldname]
                 new_field = self.current_field_from_key(key, fieldname)
                 # Have they _added_ a through= ?
                 if auto_through(old_field) and not auto_through(new_field):
                     yield ("DeleteM2M", {"model": self.old_orm[key], "field": old_field})
                 # Have they _removed_ a through= ?
                 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 {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 = frozenset(tuple(o) for o in old_together)
                 new_together = frozenset(tuple(n) for n in new_together)
                 # See if any appeared or disappeared
                 disappeared = old_together.difference(new_together)
                 appeared = new_together.difference(old_together)
                 for item in disappeared:
                     yield (del_operation, {
                         "model": self.old_orm[key],
                         "fields": [self.old_orm[key + ":" + x] for x in item],
                     })
                 for item in appeared:
                     yield (add_operation, {
                         "model": self.current_model_from_key(key),
                         "fields": [self.current_field_from_key(key, x) for x in item],
                     })
Exemplo n.º 4
0
 def get_changes(self):
     """
     Returns the difference between the old and new sets of models as a 5-tuple:
     added_models, deleted_models, added_fields, deleted_fields, changed_fields
     """
     
     deleted_models = set()
     
     # See if anything's vanished
     for key in self.old_defs:
         if key not in self.new_defs:
             # We shouldn't delete it if it was managed=False
             if self.old_defs[key].get("Meta", {}).get("managed", "True") != "False":
                 # Alright, delete it.
                 yield ("DeleteModel", {
                     "model": self.old_orm[key], 
                     "model_def": self.split_model_def(self.old_orm[key], self.old_defs[key])[0],
                 })
             # We always add it in here so we ignore it later
             deleted_models.add(key)
     
     # Or appeared
     for key in self.new_defs:
         if key not in self.old_defs:
             # We shouldn't add it if it's managed=False
             if self.new_defs[key].get("Meta", {}).get("managed", "True") != "False":
                 yield ("AddModel", {
                     "model": self.current_model_from_key(key), 
                     "model_def": self.split_model_def(self.current_model_from_key(key), self.new_defs[key])[0],
                 })
     
     # Now, for every model that's stayed the same, check its fields.
     for key in self.old_defs:
         if key not in deleted_models:
             
             still_there = set()
             
             old_fields, old_meta, old_m2ms = self.split_model_def(self.old_orm[key], self.old_defs[key])
             new_fields, new_meta, new_m2ms = self.split_model_def(self.current_model_from_key(key), self.new_defs[key])
             
             # Find fields that have vanished.
             for fieldname in old_fields:
                 if fieldname not in new_fields:
                     yield ("DeleteField", {"model": self.old_orm[key], "field": self.old_orm[key + ":" + fieldname], "field_def": old_fields[fieldname]})
             
             # And ones that have appeared
             for fieldname in new_fields:
                 if fieldname not in old_fields:
                     yield ("AddField", {"model": self.current_model_from_key(key), "field": self.current_field_from_key(key, fieldname), "field_def": new_fields[fieldname]})
             
             # Find M2Ms that have vanished
             for fieldname in old_m2ms:
                 if fieldname not in new_m2ms:
                     # Only delete its stuff if it wasn't a through=.
                     field = self.old_orm[key + ":" + fieldname]
                     if auto_through(field):
                         yield ("DeleteM2M", {"model": self.old_orm[key], "field": field})
             
             # Find M2Ms that have appeared
             for fieldname in new_m2ms:
                 if fieldname not in old_m2ms:
                     # Only create its stuff if it wasn't a through=.
                     field = self.current_field_from_key(key, fieldname)
                     if auto_through(field):
                         yield ("AddM2M", {"model": self.current_model_from_key(key), "field": field})
             
             # For the ones that exist in both models, see if they were changed
             for fieldname in set(old_fields).intersection(set(new_fields)):
                 if self.different_attributes(
                  remove_useless_attributes(old_fields[fieldname], True),
                  remove_useless_attributes(new_fields[fieldname], True)):
                     yield ("ChangeField", {
                         "model": self.current_model_from_key(key),
                         "old_field": self.old_orm[key + ":" + fieldname],
                         "new_field": self.current_field_from_key(key, fieldname),
                         "old_def": old_fields[fieldname],
                         "new_def": new_fields[fieldname],
                     })
                 # See if their uniques have changed
                 old_field = self.old_orm[key + ":" + fieldname]
                 new_field = self.current_field_from_key(key, fieldname)
                 if old_field.unique != new_field.unique:
                     # Make sure we look at the one explicitly given to see what happened
                     if new_field.unique:
                         yield ("AddUnique", {
                             "model": self.current_model_from_key(key),
                             "fields": [self.current_field_from_key(key, fieldname)],
                         })
                     else:
                         yield ("DeleteUnique", {
                             "model": self.old_orm[key],
                             "fields": [self.old_orm[key + ":" + fieldname]],
                         })
             
             # See if there's any M2Ms that have changed.
             for fieldname in set(old_m2ms).intersection(set(new_m2ms)):
                 old_field = self.old_orm[key + ":" + fieldname]
                 new_field = self.current_field_from_key(key, fieldname)
                 # Have they _added_ a through= ?
                 if auto_through(old_field) and not auto_through(new_field):
                     yield ("DeleteM2M", {"model": self.old_orm[key], "field": old_field})
                 # Have they _removed_ a through= ?
                 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], basestring):
                 old_unique_together = [old_unique_together]
             if new_unique_together and isinstance(new_unique_together[0], basestring):
                 new_unique_together = [new_unique_together]
             old_unique_together = map(set, old_unique_together)
             new_unique_together = 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],
                     })