Bug or Proposal: makemigrations and unmanaged models

This is either a bug with the makemigrations command, or I believe we may want to reconsider how we deal with unmanaged models and the tracking of their changes with migrations. For what it’s worth, I’m talking specifically here with the makemigrations command and migrations files themselves - as I am aware they are distinctly different from the process that happens when someone runs migrate and the database is altered.

The Issue/Bug: Changes to unmanaged models are not being recognized by makemigrations. If those same changes are applied to a managed model, makemigrations recognizes the changes.

The makemigrations command tracks the Creation of an unmanaged model, the Deletion of an unmanaged model, but does not track changes to the attributes/fields of an unmanaged model (see example below for more depth).

Concerns/Discussion: If we are including unmanaged models in migration files ‘for somewhat of a historical reference of changes to a model’ (my rough interpretation) then all changes should be recorded similarly for both managed & unmanaged models. Otherwise, what is the point in recording anything for the unmanaged models?

Because if you create an unmanaged model, make the initial migration and then have to change a field on it, makemigrations will not detect the change you made. At that point, your migrations (the ‘historical reference’) are out of sync with the current reality of the model.

That’s what makes me think it may be a bug. If we’re tracking managed model alterations via migrations and we’re tracking creation and deletion of unmanaged models via migrations, we should either:

  • a. track alterations to unmanaged models via migrations (for historical purpose)
  • b. not include unmanaged models in migrations at all (honestly, I would prefer this option)

I have some examples if I am being unclear.
For this example let’s assume we have two models: UnmanagedThing and ManagedThing (unmanaged and managed, respectively)

class UnmanagedThing(models.Model):
    """
    This model represents an unmanaged Thing.
    """

    id = models.IntegerField(
        db_column="thingID", primary_key=True
    )
    thing_number = models.CharField(max_length=16, db_column="thingNumber")
    thing_name = models.CharField(max_length=50, db_column="thingName")
    grade_level = models.CharField(max_length=2, db_column="gradeLevel")

    class Meta:
        """Meta options for the UnmanagedThing model."""

        verbose_name = "Unmanaged Thing"
        verbose_name_plural = "Unmanaged Things"
        managed = False
        db_table = "some_table_name"

    def __str__(self) -> str:
        """Return the course name."""
        return f"{self.thing_name}"

class ManagedThing(models.Model):
    """
    This model represents an unmanaged Thing.
    """

    id = models.IntegerField(
        db_column="thingID", primary_key=True
    )
    thing_number = models.CharField(max_length=16, db_column="thingNumber")
    thing_name = models.CharField(max_length=50, db_column="thingName")
    grade_level = models.CharField(max_length=2, db_column="gradeLevel")

    class Meta:
        """Meta options for the UnmanagedThing model."""

        verbose_name = "Unmanaged Thing"
        verbose_name_plural = "Unmanaged Things"

    def __str__(self) -> str:
        """Return the course name."""
        return f"{self.thing_name}"

If I run makemigrations I get the expected 0001... migration file and I see both models created with their respective fields/options.

If I change thing_name to be an IntegerField on the managed model and run makemigrations, I see a 0002... migration file created with the expected ‘AlterField’ inside of it.

If I change thing_name to be an IntegerField on the unmanaged model and run makemigrations, I see “No changes detected” and nothing is created. However, now there is a disconnect between my model and what is recorded in migrations.

Everything else works similarly between the two; if I delete either one, it’s recorded in a migration file. If I change the unmanaged model to ‘managed’, it creates a migration file for that (and subsequently begins tracking changes as expected with makemigrations

So I guess ultimately: is this a bug? It sure feels like we should be tracking changes the same between them. Per the django docs: " You should think of migrations as a version control system for your database schema. makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits"; it’s not behaving that way currently, so I can’t see it’s current behavior in line with that definition.

Thanks for putting this together.

I do believe that this is qualifies as a bug as I would expect managed model changes to be tracked but noop at application time.

This lack of proper tracking might explain surface bugs like this one.