自定义用户 class 和 Postgresql 模式的 Django "relation does not exist" 错误

Django "relation does not exist" error with custom user class and Postgresql schemas

在使用 Postgresql 数据库的 Django 项目上 运行 createsuperuser 时,我收到 django.db.utils.ProgrammingError: relation "user" does not exist 错误。

我编写了以下数据库路由器以指示 table user(基于 AbstractUser class 的自定义扩展)在模式中users。即使这样,Django 也找不到它。

from myapp.models import Class1, Class2, Class3
from users.models import User
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session

# Include here classes (i.e. tables) that belongs to the "myapp" schema
ROUTED_MODELS_MYAPP = [Class1, Class2, Class3]

# Include here classes (i.e. tables) that belongs to the "users" schema
ROUTED_MODELS_USERS = [User, LogEntry, ContentType, Session] #classes for custom user model, and django tables `django_admin_log`, `django_content_type`, `django_session` tables
# classes for the following table names still missing from list: `auth_group`, `auth_group_permissions`, `auth_permission`.

class MyDBRouter(object):
    """
    A router to place DB queries into correct schema depending on considered tables.
    Sources: 
        
        https://www.amvtek.com/blog/posts/2014/Jun/13/accessing-multiple-postgres-schemas-from-django/
    """
    def db_for_read(self, model, **hints):
        if model in ROUTED_MODELS_MYAPP:
            return 'myapp'
        elif model in ROUTED_MODELS_USERS:
            return 'users'
        return None

    def db_for_write(self, model, **hints):
        if model in ROUTED_MODELS_MYAPP:
            return 'myapp'
        elif model in ROUTED_MODELS_USERS:
            return 'users'        
        return None   

路由器适用于与身份验证无关的其他 tables,所以我怀疑这与我迁移用户 class 时 Django 自动创建的其他 tables 有关第一次 (auth_group, auth_group_permissions, auth_permission, django_admin_log, django_content_type, django_session).

不过,我不确定:

  1. 如果路由器应该像我那样写(一系列 if/elif) --> 是否有更好的方法为多个模式编写路由器?
  2. 对应的型号名称有哪些 上面列出的 Django tables。 --> 通过查看 django/contrib 目录,我能够猜出最后三个 class 名称(django_admin_logdjango_content_typedjango_session),但我怎么应该找到 auth_groupauth_group_permissionsauth_permission?

编辑: 根据@Kevin 的评论,我尝试根据 app_labels 而不是模型名称编写路由器,如文档中所示,使每个涉及的应用程序一个路由器。我还尝试在我的用户 class 的 Meta class 中手动指定 app_label(即 app_label = 'users')。

然而,当我在 createsuperuser 中输入用户名时,原始错误 (django.db.utils.ProgrammingError: relation "user" does not exist) 仍然存在。我还应该如何处理路由器中的这种情况?

#Route all models in admin application, cf. https://docs.djangoproject.com/en/2.1/topics/db/multi-db/
class AdminRouter:
    """
    A router to control all database operations on models in the admin application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read admin models go to users.
        """
        if model._meta.app_label == 'admin':
            return 'users'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write admin models go to users.
        """
        if model._meta.app_label == 'admin':
            return 'users'
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the admin app only appears in the 'users' database.
        """
        if app_label == 'admin':
            return db == 'users'
        return None

class AuthRouter:
    """
    A router to control all database operations on models in the
    auth application.
    """
    <similar to previous router>

class ContentTypeRouter:
    """
    A router to control all database operations on models in the
    contenttype application.
    """
    <similar to previous router>

class SessionRouter:
    """
    A router to control all database operations on models in the
    sessionapplication.
    """
    <similar to previous router>

#Route all models in users application, cf. https://docs.djangoproject.com/en/2.1/topics/db/multi-db/
class UsersRouter:
    """
    A router to control all database operations on models in the users application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read user models go to users.
        """
        if model._meta.app_label == 'users':
            return 'users'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write user models go to users.
        """
        if model._meta.app_label == 'users':
            return 'users'
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the user app only appears in the 'users' database.
        """
        if app_label == 'users':
            return db == 'users'
        return None

然后,我按以下顺序从 settings.py 调用它们:

DATABASE_ROUTERS = (
                    'urbio.dbrouters.AdminRouter',
                    'urbio.dbrouters.AuthRouter', 
                    'urbio.dbrouters.ContentTypeRouter', 
                    'urbio.dbrouters.SessionRouter', 
                    'urbio.dbrouters.UsersRouter',
                    )

解决方案是指定 --database 标志并在 运行 createsuperuser 命令时指向正确的模式:

python manage.py createsuperuser --database users

此外,在this answer之后,settings.py中的数据库定义是:

DATABASES = {

    'default': 
            {},

    'schema1': 
            {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'OPTIONS': {
                            'options': '-c search_path=schema1'
                        },
            'NAME': 'mydbname',
            'USER': 'myusername',
            'PASSWORD': '***',
            'HOST': 'my.host.address',
            'PORT': '5432',
            },

    'users': 
            {
            'ENGINE': 'django.db.backends.postgresql_psycopg2',
            'OPTIONS': {
                            'options': '-c search_path=users'
                        },
            'NAME': 'mydbname',
            'USER': 'myusername',
            'PASSWORD': '***',
            'HOST': 'my.host.address',
            'PORT': '5432',
            }

}