I am using supabase postgresql along with django. By default it uses the public schema, but now I want to link the user_id field in model to auth.users.id where auth is the schema, users is the table name and id is the UUID field
Models definition here
# auth/models.py
class SupabaseUser(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
verbose_name="User ID",
help_text="Supabase managed user id",
editable=False,
)
class Meta:
managed = False
db_table = "users"
# myapp/models.py
class MyModel(models.Model):
user = models.ForeignKey(
SupabaseUser,
on_delete=models.CASCADE,
verbose_name="Supabase User",
help_text="Supabase user associated with the account",
null=False,
)
In the settings I have two database connections
DATABASES = {
"default": dj_database_url.config(),
"supabase_auth": dj_database_url.config(),
}
DATABASES["supabase_auth"]["OPTIONS"] = {
"options": "-c search_path=auth",
}
And of course model router is configured as
from django.db.models import Model
from django.db.models.options import Options
class ModelRouter:
@staticmethod
def db_for_read(model: Model, **kwargs):
return ModelRouter._get_db_schema(model._meta)
@staticmethod
def db_for_write(model: Model, **kwargs):
return ModelRouter._get_db_schema(model._meta)
@staticmethod
def allow_migrate(db, app_label, model: Model, model_name=None, **kwargs):
return True
@staticmethod
def _get_db_schema(options: Options) -> str:
if options.app_label == "auth":
return "supabase_auth"
return "default"
When I use the ./manage.py shell
and run the following script, it works
from auth.models import SupabaseUser
assert SupabaseUser.objects.count() == 1
But when I apply migration for myapp
, I am getting the following error
$ ./manage.py makemigration myapp && ./manage.py migrate myapp
Operations to perform:
Apply all migrations: myapp
Running migrations:
Applying myapp.0013_mymodel_user...Traceback (most recent call last):
File "/mnt/Projects/myapp/backend/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 103, in _execute
return self.cursor.execute(sql)
^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.UndefinedTable: relation "users" does not exist
....
snip
....
django.db.utils.ProgrammingError: relation "users" does not exist
Actually I was able to solve this using migrations.RunSQL
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("auth", "0001_initial"),
("myapp", "0012_alter_mymodel_some_field"),
]
operations = [
migrations.RunSQL(
sql=(
"ALTER TABLE myapp_mymodel ADD COLUMN user_id UUID;",
"ALTER TABLE myapp_mymodel ADD CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE;",
),
reverse_sql=(
"ALTER TABLE myapp_mymodel DROP CONSTRAINT fk_user_id;",
"ALTER TABLE myapp_mymodel DROP COLUMN user_id;",
),
),
]
In this sql
field is executed whenever the migration is applied and reverse_sql
is executed whenever the migration is reverted. Reverse SQL is optional, but it is generally added to cleanup the mess from migrations.