def field_dependencies(field, checked_models=None): checked_models = checked_models or set() depends = set() 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
def get_value(field, descriptor): """ Gets an attribute value from a Field instance and formats it. """ attrname, options = descriptor # If the options say it's not a attribute name but a real value, use that. if options.get('is_value', False): value = attrname else: try: value = get_attribute(field, attrname) except AttributeError: if options.get("ignore_missing", False): raise IsDefault else: raise # Lazy-eval functions get eval'd. if isinstance(value, Promise): value = text_type(value) # If the value is the same as the default, omit it for clarity if "default" in options and value == options['default']: raise IsDefault # If there's an ignore_if, use it if "ignore_if" in options: if get_attribute(field, options['ignore_if']): raise IsDefault # If there's an ignore_if_auto_through which is True, use it if options.get("ignore_if_auto_through", False): if auto_through(field): raise IsDefault # Some default values need to be gotten from an attribute too. if "default_attr" in options: default_value = get_attribute(field, options['default_attr']) if value == default_value: raise IsDefault # Some are made from a formatting string and several attrs (e.g. db_table) if "default_attr_concat" in options: format, attrs = options['default_attr_concat'][0], options[ 'default_attr_concat'][1:] default_value = format % tuple( map(lambda x: get_attribute(field, x), attrs)) if value == default_value: raise IsDefault # Clean and return the value return value_clean(value, options)
def get_value(field, descriptor): """ Gets an attribute value from a Field instance and formats it. """ attrname, options = descriptor # If the options say it's not a attribute name but a real value, use that. if options.get('is_value', False): value = attrname else: try: value = get_attribute(field, attrname) except AttributeError: if options.get("ignore_missing", False): raise IsDefault else: raise # Lazy-eval functions get eval'd. if isinstance(value, Promise): value = unicode(value) # If the value is the same as the default, omit it for clarity if "default" in options and value == options['default']: raise IsDefault # If there's an ignore_if, use it if "ignore_if" in options: if get_attribute(field, options['ignore_if']): raise IsDefault # If there's an ignore_if_auto_through which is True, use it if options.get("ignore_if_auto_through", False): if auto_through(field): raise IsDefault # Some default values need to be gotten from an attribute too. if "default_attr" in options: default_value = get_attribute(field, options['default_attr']) if value == default_value: raise IsDefault # Some are made from a formatting string and several attrs (e.g. db_table) if "default_attr_concat" in options: format, attrs = options['default_attr_concat'][0], options['default_attr_concat'][1:] default_value = format % tuple(map(lambda x: get_attribute(field, x), attrs)) if value == default_value: raise IsDefault # Clean and return the value return value_clean(value, options)
def get_value(field, descriptor): """ Gets an attribute value from a Field instance and formats it. """ attrname, options = descriptor # If the options say it's not a attribute name but a real value, use that. if options.get('is_value', False): value = attrname else: try: value = get_attribute(field, attrname) except AttributeError: if options.get("ignore_missing", False): raise IsDefault else: raise # Lazy-eval functions get eval'd. if isinstance(value, Promise): value = unicode(value) # If the value is the same as the default, omit it for clarity if "default" in options and value == options['default']: raise IsDefault # If there's an ignore_if, use it if "ignore_if" in options: if get_attribute(field, options['ignore_if']): raise IsDefault # If there's an ignore_if_auto_through which is True, use it if options.get("ignore_if_auto_through", False): if auto_through(field): raise IsDefault # Some default values need to be gotten from an attribute too. if "default_attr" in options: default_value = get_attribute(field, options['default_attr']) if value == default_value: raise IsDefault # Some are made from a formatting string and several attrs (e.g. db_table) if "default_attr_concat" in options: format, attrs = options['default_attr_concat'][0], options['default_attr_concat'][1:] default_value = format % tuple(map(lambda x: get_attribute(field, x), attrs)) if value == default_value: raise IsDefault # Callables get called. if callable(value) and not isinstance(value, ModelBase): # Datetime.datetime.now is special, as we can access it from the eval # context (and because it changes all the time; people will file bugs otherwise). if value == datetime.datetime.now: return "datetime.datetime.now" if value == datetime.datetime.utcnow: return "datetime.datetime.utcnow" if value == datetime.date.today: return "datetime.date.today" # All other callables get called. value = value() # Models get their own special repr() if isinstance(value, ModelBase): # If it's a proxy model, follow it back to its non-proxy parent if getattr(value._meta, "proxy", False): value = value._meta.proxy_for_model return "orm['%s.%s']" % (value._meta.app_label, value._meta.object_name) # As do model instances if isinstance(value, Model): if options.get("ignore_dynamics", False): raise IsDefault return "orm['%s.%s'].objects.get(pk=%r)" % (value.__class__._meta.app_label, value.__class__._meta.object_name, value.pk) # Make sure Decimal is converted down into a string if isinstance(value, decimal.Decimal): value = str(value) # datetime_safe has an improper repr value if isinstance(value, datetime_safe.datetime): value = datetime.datetime(*value.utctimetuple()[:7]) if isinstance(value, datetime_safe.date): value = datetime.date(*value.timetuple()[:3]) # Now, apply the converter func if there is one if "converter" in options: value = options['converter'](value) # Return the final value return repr(value)
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 ], })
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], })
def get_value(field, descriptor): """ Gets an attribute value from a Field instance and formats it. """ attrname, options = descriptor # If the options say it's not a attribute name but a real value, use that. if options.get('is_value', False): value = attrname else: value = get_attribute(field, attrname) # Lazy-eval functions get eval'd. if isinstance(value, Promise): value = unicode(value) # If the value is the same as the default, omit it for clarity if "default" in options and value == options['default']: raise IsDefault # If there's an ignore_if, use it if "ignore_if" in options: if get_attribute(field, options['ignore_if']): raise IsDefault # If there's an ignore_if_auto_through which is True, use it if options.get("ignore_if_auto_through", False): if auto_through(field): raise IsDefault # Some default values need to be gotten from an attribute too. if "default_attr" in options: default_value = get_attribute(field, options['default_attr']) if value == default_value: raise IsDefault # Some are made from a formatting string and several attrs (e.g. db_table) if "default_attr_concat" in options: format, attrs = options['default_attr_concat'][0], options['default_attr_concat'][1:] default_value = format % tuple(map(lambda x: get_attribute(field, x), attrs)) if value == default_value: raise IsDefault # Callables get called. if callable(value) and not isinstance(value, ModelBase): # Datetime.datetime.now is special, as we can access it from the eval # context (and because it changes all the time; people will file bugs otherwise). if value == datetime.datetime.now: return "datetime.datetime.now" if value == datetime.datetime.utcnow: return "datetime.datetime.utcnow" if value == datetime.date.today: return "datetime.date.today" # All other callables get called. value = value() # Models get their own special repr() if isinstance(value, ModelBase): # If it's a proxy model, follow it back to its non-proxy parent if getattr(value._meta, "proxy", False): value = value._meta.proxy_for_model return "orm['%s.%s']" % (value._meta.app_label, value._meta.object_name) # As do model instances if isinstance(value, Model): return "orm['%s.%s'].objects.get(pk=%r)" % (value.__class__._meta.app_label, value.__class__._meta.object_name, value.pk) # Make sure Decimal is converted down into a string if isinstance(value, decimal.Decimal): value = str(value) # Now, apply the converter func if there is one if "converter" in options: value = options['converter'](value) # Return the final value return repr(value)
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], }, )
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], })