3

So i was trying to deploy a django app to heroku which works fine locally.

Though the deployment process completes successfully but the migrate command gives an error.

django.db.migrations.exceptions.NodeNotFoundError: Migration accounts.0001_initial dependencies reference nonexistent parent node ('auth', '0013_alter_user_email')

Here is my migration file;

import accounts.models
from django.conf import settings
import django.contrib.gis.db.models.fields
from django.db import migrations, models
import django.db.models.deletion
from django.contrib.postgres.operations import CreateExtension


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('auth', '0013_alter_user_email'),
    ]

    operations = [...]

From discussion in the comments it seems that the migration 0013_alter_user_email does not actually exist in the auth app. It is generated because I modify the user model by accessing it during runtime using User._meta.get_field('email').unique to make the email field unique.

Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
Irfan wani
  • 4,084
  • 2
  • 19
  • 34
  • did you try Deleting all your migrations and then re-migrate again ?? – SANGEETH SUBRAMONIAM Jul 30 '21 at 02:09
  • Yeah tried it. It works fine locally. – Irfan wani Jul 30 '21 at 02:14
  • Where is the `auth` app? Did you include it in your deployment? – Brian Destura Jul 30 '21 at 03:35
  • it is from django (django.contrib.auth). So i think it should be installed with django – Irfan wani Jul 30 '21 at 03:36
  • @bdbd though it comes with django, but i also think that there is some problem with its installation, but found nothing related to it. Do you know how to get the list of all dependencies installed on heroku – Irfan wani Jul 30 '21 at 03:43
  • 1
    Sorry no I don't. One thing though, you can try to change the dependency to the `0001` of `auth`. I remember vaguely having a similar problem which doesnt happen locally. I fixed it by loading the dependency first by using its first migration. If it doesn't help, can you show the rest of the migration file? – Brian Destura Jul 30 '21 at 03:48
  • No i think you are wrong. If you check the django docs, it says that we need to put the last migration file as the dependency from an app (and also it makes more sense as last file will be the latest one). still i tried it but got error as expected. Also i only have single migration file which i already uploaded. – Irfan wani Jul 30 '21 at 03:53
  • did you have some data on your model and you just remove the user email section – Shreyash mishra Jul 30 '21 at 04:02
  • remove pyc file from migration folder and then try it again – Shreyash mishra Jul 30 '21 at 04:03
  • Are you talking about [this](https://docs.djangoproject.com/en/3.2/topics/migrations/#accessing-models-from-other-apps)? It's not a hard rule that you need to use the last migration, only the relevant migration of the dependency with the models that you need. Otherwise you will have to always update all migrations to use the last migration of a dependency all the time. But if you don't want to try it, that's fine :) – Brian Destura Jul 30 '21 at 04:04
  • @Shreyash i don't have any pyc files there – Irfan wani Jul 30 '21 at 04:10
  • I again want to mention that it works perfectly locally – Irfan wani Jul 30 '21 at 04:28
  • What I don't understand is _where_ did you get a migration `0013_alter_user_email` in the `auth` app from? The `auth` app doesn't have such a migration (The last migration in the `auth` app is currently `0012_alter_user_first_name_max_length`)... Can you show your user model if you are using a custom one? – Abdul Aziz Barkat Jul 30 '21 at 04:32
  • @AbdulAzizBarkat i checked checked auth app manually and it has `0013_alter_user_email` as the last migration. `0012_alter_user_first_name_max_length` is 2nd last – Irfan wani Jul 30 '21 at 04:36
  • @Irfanwani no, `auth` has no such migration (See [GitHub](https://github.com/django/django/tree/main/django/contrib/auth/migrations))! You seem to have done some modifications locally. Do you use a custom user model? If so please show it in the question. – Abdul Aziz Barkat Jul 30 '21 at 04:37
  • I don't use any custum user model. What can be the other cases where a migration is added?? Can adding extra_kwargs on any of the fields in User model from our serializer do that?? – Irfan wani Jul 30 '21 at 04:43
  • @Irfanwani try this maybe this will solve your problem https://stackoverflow.com/a/38502190/14777930 – Shreyash mishra Jul 30 '21 at 04:51
  • @Shreyash no that doesn't work – Irfan wani Jul 30 '21 at 05:04
  • @AbdulAzizBarkat i am using `User._meta.get_field('email').unique` to make the email field unique and i think this is what creates that extra migration – Irfan wani Jul 30 '21 at 05:42
  • I nearly found the solution. Yes it is `User._meta.get_field('email').unique` which creates the extra migration but now the problem is how can i send that migration file to heroku. The problem is to make that migration file to appear in the auth app on server – Irfan wani Jul 30 '21 at 06:51

1 Answers1

1

You are modifying the user model at runtime causing a migration to be generated for it. This way of doing things is called monkey patching and it is a bad practice. django.contrib.auth can be said to be a 3rd party application and modifying it like this can have adverse effects one of which you are currently experiencing. What happened is that you modified the user model, and it generated a migration locally in the Django package. Since this is a package it's not part of your code and is installed separately. You cannot even run makemigrations on Heroku to solve this (not a recommended solution anyway) since any filesystem changes on Heroku are temporary and will get deleted when the dyno loads up.

Django allows one to customize authentication [Django docs] / user model very easily. In general as a best practice in Django one should always use a custom user model when starting a project.

The solution for you would be to delete all your migrations, drop the database, reinstall Django to make sure it is clean and unmodified and use a custom user model as described above:

from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _


class User(AbstractUser):
    email = models.EmailField(_('email address'), blank=True, unique=True)

Set this as AUTH_USER_MODEL [Django dosc] in the settings. There will be other changes needed too like registering the model for the admin site, changing some of the forms in the ModelAdmin (i.e. UserAdmin) and views and refactoring your code.

Abdul Aziz Barkat
  • 19,475
  • 3
  • 20
  • 33
  • Here, i just want to make email unique. So what if i write some conditions like filtering the email recieved from the user and checking if that exists and then allowing only new ones. Would that be a good practice. – Irfan wani Jul 30 '21 at 10:05
  • 1
    @Irfanwani Yes you can write some validation code yourself if you want, although setting `unique=True` should make Django forms, etc. do that automatically. It would be best to use a custom user model. – Abdul Aziz Barkat Jul 30 '21 at 10:07
  • Doing the same thing. Thanks for the help. – Irfan wani Jul 30 '21 at 10:12
  • BTW is `_('email address')` used by you just a verbose name or does it has some special meaning – Irfan wani Jul 30 '21 at 10:13
  • 1
    @Irfanwani its the verbose name just using `_` to keep it translatable. It's copied directly from the source. `_` if you see the import above is actually `django.utils.translation.gettext_lazy` so if translations are enabled and used then the verbose name will get translated to the active language. – Abdul Aziz Barkat Jul 30 '21 at 10:15