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.
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
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.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
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.