Code

Opened 2 years ago

Closed 2 years ago

Last modified 2 years ago

#17863 closed Bug (wontfix)

Storing user and user profile in different databases?

Reported by: Henning Kage <henning.kage@…> Owned by: nobody
Component: contrib.auth Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I am not sure, if this is a possible bug or a wanted feature. I want to outsource all tables from a Django application in a separate database. This includes the user profile, that I want to be stored in the app's own database. I am using the following minimal project, to reproduce the traceback:

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', 
        'NAME': 'default.db', 
    },
    'app': {
        'ENGINE': 'django.db.backends.sqlite3', 
        'NAME': 'app.db', 
    }
    
}
DATABASE_ROUTERS = ['app.router.Router']
INSTALLED_APPS = [
  # ...
  'app',
  ]
AUTH_PROFILE_MODULE = 'app.UserProfile'

app/models.py

from django.contrib.auth.models import User
from django.db import models

class UserProfile(models.Model):
    
    user = models.ForeignKey(User)

app/router.py

# -*- coding: utf-8 -*-

class Router(object):

    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'app':
            return 'app'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'app':
            return 'app'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        if (obj1._meta.app_label == 'app'
            or obj2._meta.app_label == 'app'):
            return True
        return None

    def allow_syncdb(self, db, model):
        if db == 'app':
            return model._meta.app_label == 'app'
        elif model._meta.app_label == 'app':
            return False
        return None

app/tests.py

from django.contrib.auth.models import User
from django.test import TestCase

from app.models import UserProfile

class SimpleTest(TestCase):

    def test_userprofile(self):
        u = User()
        u.save()
        p = UserProfile()
        p.user = u
        p.save()
        u.get_profile()

Although it is possible to specify a custom user profile class, Django always assumes, that the user profile model is saved in the same database Django stores the auth_user table. The unittest will result in the following traceback by calling the user's profile with User.get_profile:

Traceback (most recent call last):
  File "/Users/henningkage/Projekte/sandbox/userprofile/app/tests.py", line 14, in test_userprofile
    u.get_profile()
  File "/Users/henningkage/Python/django_multi_db/src/django/django/contrib/auth/models.py", line 401, in get_profile
    self._state.db).get(user__id__exact=self.id)
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/models/query.py", line 361, in get
    num = len(clone)
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/models/query.py", line 85, in __len__
    self._result_cache = list(self.iterator())
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/models/query.py", line 291, in iterator
    for row in compiler.results_iter():
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/models/sql/compiler.py", line 763, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/models/sql/compiler.py", line 818, in execute_sql
    cursor.execute(sql, params)
  File "/Users/henningkage/Python/django_multi_db/src/django/django/db/backends/sqlite3/base.py", line 337, in execute
    return Database.Cursor.execute(self, query, params)
DatabaseError: no such table: app_userprofile

Does Django needs the user and user profile model in the same database as foreign keys across multiple databases are not possible? My attached (minmal) change will return a valid user profile model instance. The only disadvantage to me is the fact, that the user profile has no real relation to the user and will only return the user's id. This would be the normal behaviour for relationships across multiple databases. But it would be possible to store the user profile in a different database than Django's internal tables. If this topic has already been discussed and I missed it, sorry for the ticket.

Attachments (1)

patch_get_user_profile.diff (1.2 KB) - added by Henning Kage <henning.kage@…> 2 years ago.
Method get_profile using db router

Download all attachments as: .zip

Change History (3)

Changed 2 years ago by Henning Kage <henning.kage@…>

Method get_profile using db router

comment:1 Changed 2 years ago by aaugustin

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to wontfix
  • Status changed from new to closed

By design, Django doesn't support foreign keys across databases. See the docs for more details.

There's no good reason to relax the data integrity requirements for user profiles, so I'm afraid you'll have to find another solution for your use case.

Thanks for the report!

comment:2 Changed 2 years ago by hkage

I knew that foreign keys across multiple database is not supported (due to reasonable disadvantages), but I thought that storing the user profile in a separate database would make sense as it could be defined in a Django application via settings. But you are right, it would result in a missing relationship between user profile and user object and would lead to the foreign key problem.

Thank your for the answer.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.