1

I am asking this question because I ran into this django.db.utils.IntegrityError: null value in column “lft” violates not-null constraint when I was using django-hordak which uses MPPT models.

Some of the solutions proposed are hacky as they go against common practice but they work.

THE BASICS

In django when writing custom migrations for example when creating baseline data.

def forward_func(apps_registry, schema_editor):
    Model = apps_registry.get_model('app_name', 'ModelName')
    Model.objects.create(**{'field_1': 'field_1', 'field_2': 'field_2')
    # Do whatever you want with my Model 

In my situation django-hordak with the account model. That .objects.create(**data) raises theIntegrityError on the DB as specified above.

Proposed solution

One of the solutions proposed solution is to import the model directly.

def forward_func(apps_registry, schema_editor):
    # Model = apps_registry.get_model('app_name', 'ModelName')
    # lets forget about importing the standard way because it will raise integrity error.

    from app_name.models import ModelName as Model
    # Now we can proceed to deal with our model without Integrity Error.
    Model.objects.create(**data)

This leaves me scared of possible undesirable side effects of importing models directly in migrations files.

As there must be very good reasons why the model is not imported that way in the migration files.

I am not looking for the reason why MPTT models fail when imported the standard way as that question has an answer here. . I want to understand particularly the logic behind importing models using apps.get_model in migration files.

unlockme
  • 3,897
  • 3
  • 28
  • 42
  • 3
    See the [full explanation](https://docs.djangoproject.com/en/2.2/topics/migrations/#historical-models). TL;DR: the registry contains a frozen version of the model at the time the migration was created, which might be quite different to how it is now. – Daniel Roseman Dec 02 '19 at 16:10
  • Thanks @Daniel Roseman, checked out the documentation. If you could write the answer to be accepted. Will be helpful. – unlockme Dec 02 '19 at 16:14

1 Answers1

2

that is a very good question, as there is in deed a very big difference.

If you import a model, you will get whatever is currently defined in your model code. But what happens if you want to remove a model? How do you make sure your migration will still work?

That's why there is apps.get_model. It will give you a "virtual" Model based on state defined by the previous migrations. Note that this is not the same as the Model in your code. It does not have any of the custom functions and behaviors you implemented unless they are part of the ORM API. In other words mostly fields and meta options like ordering and co.

Please also note that the correct signature is apps not app_registry. It should not be confused with the app registry in django.apps.apps. It has a get_model method too. This one however will return the model in your current code.

I know it can be all a bit confusing at first (and even later). I would suggest following a simple rule. Do not import any of your own code into a migration. Backport behavior into the migration, if you must.

I hope that distinction helps you a bit. Please leave me a comment if you have further questions. I am happy to extend my answer.

Best -Joe

codingjoe
  • 1,210
  • 15
  • 32
  • 1
    I think your answer is very good. Could you include a scenario like moved model from App A to App B and explain how that would fail. I can already see how it would. Just an illustration would improve the answer. – unlockme Dec 02 '19 at 16:25
  • @unlockme good point, I actually wanted to write `remove` not move. I updated my answer. Should I still add an example for that? – codingjoe Dec 02 '19 at 16:34
  • An example would be good but this is sufficient for a person who is reasonable with what they do. – unlockme Dec 02 '19 at 16:43