Debugging a failed migration

G’day all,

I have a branch in my Django project, it’s called otp and it is for offering TOTP authentication to users. I last worked on this branch some weeks ago and I was running some of the tests for features on which I was working. The tests were working well for the entire app.

I’m using the Django OTP package to provide the TOTP functionality and so far things are working well. My project runs, I can login with TOTP and the entire project app looks to be perfectly fine. Running my tests is a completely different story.

Running manage.py test user_auth generates the following exception (debug turned on)

2021-10-01 21:57:18,884 django.db.backends.schema DEBUG    CREATE TABLE "django_otp_staticdevice" ("id" serial NOT NULL PRIMARY KEY, "user_id" uuid NOT NULL, "name" varchar(64) NOT NULL, "confirmed" boolean NOT NULL, "throttling_failure_timestamp" timestamp with time zone NULL, "throttling_failure_count" integer NOT NULL CHECK ("throttling_failure_count" >= 0)); (params None)
2021-10-01 21:57:18,891 django.db.backends.schema DEBUG    CREATE TABLE "django_otp_statictoken" ("id" serial NOT NULL PRIMARY KEY, "device_id" integer NOT NULL, "token" varchar(16) NOT NULL); (params None)
2021-10-01 21:57:18,894 django.db.backends.schema DEBUG    ALTER TABLE "django_otp_staticdevice" ADD CONSTRAINT "django_otp_staticdevice_user_id_9631127e_fk_users_customuser_id" FOREIGN KEY ("user_id") REFERENCES "users_customuser" ("id") DEFERRABLE INITIALLY DEFERRED; (params ())
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "users_customuser" does not exist` ​

If I run my tests for some other project specific apps, then that app’s tests run just fine. If I run tests in either of the apps registration or user_auth then I get the above message.

Now, it seems quite clear what the issue is, and that is the django-otp package migration’s are trying to reference users.customuser which doesn’t exist.

Where I’m stumped is that I can’t work out why it is happening for some apps and not for others, and it also isn’t happening in my main development branch where I have the django_otp and django_otp.plugins.otp_totp installed, albeit it without the code which provides the TOTP login and the corresponding tests.

The main difference between the apps other than the views and tests is that there is one migration which has been made in the otp branch. The model is for providing a token post username and password auth. The token is used to permit a user to attempt to verify her TOTP device. The model looks like this:

class OTPAllowedToken(Token):  # token is a class provided by the app registration
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)

    def key_expired(self):
        expiration_date = self.created + timedelta(
            minutes=settings.POST_LOGIN_VALIDITY
        )
        return expiration_date <= timezone.now()

I had already created a Token model in my registration app, and it is this Token from which my OTPAllowedToken inherits.

class Token(models.Model):
    key = models.CharField(_("Key"), max_length=40, primary_key=True)
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        related_name="token",
        on_delete=models.CASCADE,
        verbose_name=_("User"),
    )
    created = models.DateTimeField(_("Created"), auto_now_add=True)

    class Meta:

        verbose_name = "Token"
        verbose_name_plural = "Tokens"
        get_latest_by = ["created"]

I fear this is in some sort of clash or migration confusement happening when the OTPAllowedToken is referencing the Token from registration.

As a test, I rolled my migration back to the migration before the OTPAllowedToken. I then declared my OTPAllowedToken as inheriting from models.Model and added the necessary attributes, effectively just copying what I already had in my Token model. This is the only migration I have made in this branch and the error remains after making the changes just described.

So that’s where I am. My app run’s well but my test suite fails because of this issue. I don’t understand why the django-otp migrations and my local app’s tests fail, and I’d love to hear any and all ideas.

Thank you.

Cheers,

Conor

Should anyone stumble across this issue, my temporary solution has been to remove these two imports

from django_otp.plugins.otp_static.models import StaticDevice, StaticToken

Without them, my tests run flawlessly, include them, and I get the above error. I’m still debugging why this is the case, but I’m not having much joy. On the bright side, OTP device creation, verification and authentication is now working very well.