This is fresh from my stack overflow post… Really could do with some help here…
I’ve created a new project from scratch. By default it uses the auth
package for user and permission management.
Given the following from official Django Docs
Using a custom user model when starting a project¶
If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you. This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises:
I am wanting to override the default User - using AbstractBaseUser.
I am trying to setup this in a multi-database environment. One database for the auth, ‘contenttypes’, ‘sessions’, and ‘admin’. And the other for my application related stuff.
Having created migrations and executed them on my databases, the auth_user
table resides in my application database - not the auth
database.
Is this expected behaviour? I was expecting - and I want the auth_user
table to reside in the auth
database.
Is there a way to make the auth_user reside in the auth
database?
Create Project
py -m venv venv
venv\scripts\activate
pip install django
django-admin startproject core .
py manage.py startapp acmeapp
acmeapp/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class Foo(models.Model):
foo_id = models.BigAutoField(primary_key=True)
name = models.TextField()
class Meta:
db_table = 'foos'
class AccountManager(BaseUserManager):
"""
The management class to handle user creation.
"""
def create_user(self, username, password=None):
if not username:
raise ValueError(_('The Username must be set'))
user = self.model(username=username)
user.set_password(password)
user.save()
return user
def create_superuser(self, username, password=None):
return self.create_user(username, password)
class Account(AbstractBaseUser):
"""
The custom user model.
"""
username = models.CharField(max_length=40, unique=True)
email = None
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['access_platforms', 'access_missions', 'access_rule_sets', 'access_mdsr']
objects = AccountManager()
def __str__(self):
return ''.join(c for c in self.username if c.isupper())
class Meta:
db_table = 'auth_user'
core/settings.py
DATABASES = {
'default': {},
'auth_db': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'auth_db.sqlite3',
"TEST": {
"NAME": "auth_db",
'DEPENDENCIES': [],
},
},
'acmeapp_db': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'acmeapp_db.sqlite3',
"TEST": {
"NAME": "acmeapp_db",
'DEPENDENCIES': [],
},
}
}
DATABASE_ROUTERS = [
'core.db_routers.AuthRouter',
'core.db_routers.AcmeAppRouter'
]
AUTH_USER_MODEL = 'acmeapp.Account'
core/db_routers
from acmeapp.models import Account
class AuthRouter:
route_app_labels = ['auth', 'contenttypes', 'sessions', 'admin']
def db_for_read(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'auth_db'
if model == Account:
return 'auth_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'auth_db'
if model == Account:
return 'auth_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in self.route_app_labels:
return db == 'auth_db'
return None
class AcmeAppRouter:
route_app_labels = ['acmeapp']
def db_for_read(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'acmeapp_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in self.route_app_labels:
return 'acmeapp_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in self.route_app_labels:
return db == 'acmeapp_db'
return None
Make and Apply Migrations
Once the above code is created, I create migrations and apply them to each database.
py manage.py makemigrations
py manage.py migrate --database=acmeapp_db
py manage.py migrate --database=auth_db
/acmeapp/migrations/0001_initial.py
# Generated by Django 4.0.4 on 2022-08-11 07:50
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Account',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('username', models.CharField(max_length=40, unique=True)),
],
options={
'db_table': 'auth_user',
},
),
migrations.CreateModel(
name='Foo',
fields=[
('mission_id', models.BigAutoField(primary_key=True, serialize=False)),
('name', models.TextField()),
('retired', models.BooleanField(default=False)),
],
options={
'db_table': 'foos',
},
),
]
Which has the two models including Account.
As a result the two databases are created and have tables as follows:
acmeapp_db.sqlite
auth_user
django_migrations
foos
sqlite_sequence
auth_db.sqlite
auth_group
auth_group_permissions
auth_permission
django_admin_log
django_content_type
django_migrations
django_session
sqlite_sequence
Research
I have not found an answer to the problem. Every blog post on setting this up never covers the what happens in this scenario.
I havent found an answer in any Django documentation either.
Curiously, https://code.djangoproject.com/wiki/MultipleDatabaseSupport
Different models/applications living on different databases, for example a ‘blog’ application on db1 and a forum application on db2. This should include the ability to assign a different database to an existing application without modifying it, e.g. telling Django where to keep the django.contrib.auth.User table.