Code

Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#16283 closed Bug (fixed)

manage.py depends on django.contrib.contenttypes et al.

Reported by: TheRoSS Owned by: nobody
Component: contrib.auth Version: 1.3
Severity: Normal Keywords:
Cc: TheRoSS Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I intended to use django.contrib.auth with my own backends, so I included 'django.contrib.auth.middleware.AuthenticationMiddleware' into 'MIDDLEWARE_CLASSES', excluded 'django.contrib.auth' from 'INSTALLED_APPS' and created my own authentication application.

But if I named my authentication application as 'project.auth', django used models from 'django.contrib.auth'.
If I gave it any other name, 'project.auth2' for example, models were mine.

project.settings contains:

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

INSTALLED_APPS = (
    'project.auth',    # DOESN'T WORK!
    'project.auth2',   # works well
)

I tracked down the source code and realized that django.db.models.loading.app_models dictionary has a record named 'auth' taken from 'django.contrib.auth' (upon middleware processing I suppose) which prevents my 'auth' application to be used.

Attachments (1)

seka.zip (1.9 KB) - added by TheRoSS 3 years ago.

Download all attachments as: .zip

Change History (12)

comment:1 Changed 3 years ago by TheRoSS

  • Cc TheRoSS added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

comment:2 Changed 3 years ago by aaugustin

  • Resolution set to invalid
  • Status changed from new to closed

This works as advertised: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps

It's very hard to remove this limitation in a backwards compatible way because Django references models in many places as <app_module>.<ModelClass>, which gets translated to <path>.<to>.<app_module>.models.<ModelClass>.

comment:3 Changed 3 years ago by TheRoSS

  • Resolution invalid deleted
  • Status changed from closed to reopened

No! The problem is that I didn't used 'django.contrib.auth' at all! But cannot name my application 'auth'.

My settings.py file:

DEBUG = True
TEMPLATE_DEBUG = DEBUG

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'seka',                      # Or path to database file if using sqlite3.
        'USER': 'root',                      # Not used with sqlite3.
        'PASSWORD': 'root',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
)

ROOT_URLCONF = 'seka.urls'

INSTALLED_APPS = (
    'seka.auth',
)

My models.py:

from django.db import models
from django.utils.translation import ugettext_lazy as _

# Create your models here.
class User (models.Model):
    username = models.CharField(_('username'), max_length=30, unique=True, help_text=_("Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters"))

Bu running 'manage.py sql auth' gives:

BEGIN;
CREATE TABLE `auth_permission` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(50) NOT NULL,
    `content_type_id` integer NOT NULL,
    `codename` varchar(100) NOT NULL,
    UNIQUE (`content_type_id`, `codename`)
)
;
CREATE TABLE `auth_group_permissions` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `group_id` integer NOT NULL,
    `permission_id` integer NOT NULL,
    UNIQUE (`group_id`, `permission_id`)
)
;
ALTER TABLE `auth_group_permissions` ADD CONSTRAINT `permission_id_refs_id_5886d21f` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`);
CREATE TABLE `auth_group` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(80) NOT NULL UNIQUE
)
;
ALTER TABLE `auth_group_permissions` ADD CONSTRAINT `group_id_refs_id_3cea63fe` FOREIGN KEY (`group_id`) REFERENCES `auth_group` (`id`);
CREATE TABLE `auth_user_user_permissions` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `user_id` integer NOT NULL,
    `permission_id` integer NOT NULL,
    UNIQUE (`user_id`, `permission_id`)
)
;
ALTER TABLE `auth_user_user_permissions` ADD CONSTRAINT `permission_id_refs_id_67e79cb` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`);
CREATE TABLE `auth_user_groups` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `user_id` integer NOT NULL,
    `group_id` integer NOT NULL,
    UNIQUE (`user_id`, `group_id`)
)
;
ALTER TABLE `auth_user_groups` ADD CONSTRAINT `group_id_refs_id_f116770` FOREIGN KEY (`group_id`) REFERENCES `auth_group` (`id`);
CREATE TABLE `auth_user` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `username` varchar(30) NOT NULL UNIQUE,
    `first_name` varchar(30) NOT NULL,
    `last_name` varchar(30) NOT NULL,
    `email` varchar(75) NOT NULL,
    `password` varchar(128) NOT NULL,
    `is_staff` bool NOT NULL,
    `is_active` bool NOT NULL,
    `is_superuser` bool NOT NULL,
    `last_login` datetime NOT NULL,
    `date_joined` datetime NOT NULL
)
;
ALTER TABLE `auth_user_user_permissions` ADD CONSTRAINT `user_id_refs_id_dfbab7d` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `auth_user_groups` ADD CONSTRAINT `user_id_refs_id_7ceef80f` FOREIGN KEY (`user_id`) REFERENCES `auth_user`(`id`);
CREATE TABLE `auth_message` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `user_id` integer NOT NULL,
    `message` longtext NOT NULL
)
;
ALTER TABLE `auth_message` ADD CONSTRAINT `user_id_refs_id_650f49a6` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
-- The following references should be added but depend on non-existent tables:
-- ALTER TABLE `auth_permission` ADD CONSTRAINT `content_type_id_refs_id_728de91f` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`);
COMMIT;

comment:4 Changed 3 years ago by aaugustin

  • Resolution set to invalid
  • Status changed from reopened to closed

tl;dr don't call your app "auth" :)


You have django.contrib.auth.middleware.AuthenticationMiddleware in MIDDLEWARE_CLASSES.

By default, this will use django.contrib.auth.backends.ModelBackend as an authentication backend (https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends).

This backend imports django.contrib.auth.models (https://code.djangoproject.com/browser/django/trunk/django/contrib/auth/backends.py)

Thus, even if you haven't declared django.contrib.auth in INSTALLED_APPS, it gets registered in the application cache, creating a conflict with your app.

I hope this helps!

comment:5 Changed 3 years ago by TheRoSS

  • Resolution invalid deleted
  • Status changed from closed to reopened

I think there is a misunderstanding between us...
I didn't used 'django.contrib.auth' at all, look at my second post, please.
There is no 'django.contrib.auth.middleware.AuthenticationMiddleware' in MIDDLEWARE_CLASSES

I raised an exeption at the beginning of 'django.contrib.auth.models'. Look at the exception stack, please:

Traceback (most recent call last):
  File "C:\home\dev\seka\seka\manage.py", line 14, in <module>
    execute_manager(settings)
  File "C:\home\python\lib\site-packages\django\core\management\__init__.py", line 438, in execute_manager
    utility.execute()
  File "C:\home\python\lib\site-packages\django\core\management\__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\home\python\lib\site-packages\django\core\management\base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "C:\home\python\lib\site-packages\django\core\management\base.py", line 219, in execute
    self.validate()
  File "C:\home\python\lib\site-packages\django\core\management\base.py", line 243, in validate
    from django.core.management.validation import get_validation_errors
  File "C:\home\python\lib\site-packages\django\core\management\validation.py", line 3, in <module>
    from django.contrib.contenttypes.generic import GenericForeignKey, GenericRelation
  File "C:\home\python\lib\site-packages\django\contrib\contenttypes\generic.py", line 13, in <module>
    from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
  File "C:\home\python\lib\site-packages\django\contrib\admin\__init__.py", line 6, in <module>
    from django.contrib.admin.sites import AdminSite, site
  File "C:\home\python\lib\site-packages\django\contrib\admin\sites.py", line 4, in <module>
    from django.contrib.admin.forms import AdminAuthenticationForm
  File "C:\home\python\lib\site-packages\django\contrib\admin\forms.py", line 4, in <module>
    from django.contrib.auth.forms import AuthenticationForm
  File "C:\home\python\lib\site-packages\django\contrib\auth\forms.py", line 1, in <module>
    from django.contrib.auth.models import User
  File "C:\home\python\lib\site-packages\django\contrib\auth\models.py", line 15, in <module>
    raise 'AUTH MODELS CALLED'

So 'auth' package was called from 'admin' package which was called from 'contenttypes' which was called from django core.

I didn't included those packages in my settings, but they were called anymore.
Is this ok?
Are the names 'auth', 'admin', 'contenttypes' magic and forbidden to use as an application names?

I attach my zipped example project with this message, hope it will be helpful.

Changed 3 years ago by TheRoSS

comment:6 Changed 3 years ago by aaugustin

  • Summary changed from my application project.auth takes models from django.contrib.auth to manage.py depends on django.contrib.contenttypes et al.
  • Triage Stage changed from Unreviewed to Accepted

Your original report contained django.contrib.auth.middleware.AuthenticationMiddleware, and I missed the fact that the example you posted later on didn't — sorry about that.

Thanks for your example. It really helped me reproduce the problem quickly. Indeed, I obtain this:

% ./manage.py sql --traceback all
Error: One or more models did not validate:
auth.permission: 'content_type' has a relation with model <class 'django.contrib.contenttypes.models.ContentType'>, which has either not been installed or is abstract.

And by raising an exception in django.contrib.auth.models, I reproduce your backtrace. It shows that manage.py depends on contenttypes, admin and auth, which is bad because django's core is supposed not to depend on contrib apps.

The root cause of the problem is the fact that since r14563 django.core.management.validation depends on django.contrib.contenttypes. I am going to update the summary to reflect this.

comment:7 Changed 3 years ago by ramiro

Seems like in the short term we'd need to undo the changes from [14563]. Long term solution for validate core management command extensibility is what #8579 asks for.

comment:8 Changed 3 years ago by jezdez

I could see the validate command calling a specific hook in the app classes (e.g. a validate method) once the app-loading branch lands.

comment:9 Changed 3 years ago by ramiro

  • Resolution set to fixed
  • Status changed from reopened to closed

In [16493]:

(The changeset message doesn't reference this ticket)

comment:11 Changed 3 years ago by ramiro

In [16541]:

[1.3.X] Reverted [14563] because it introduced a dependency from core on a contrib app (contenttypes). Fixes #16283, Refs #3055. Thanks TheRoSS for the report and Aymeric Augustin for finding the problem.

This caused models shipped with some contrib apps to pollute the namespace when user's apps had the same name (e.g. auth, sites), even when these contrib apps weren't installed.

This undesired loading of contrib apps happened when model validation was executed, for example when running management commands that set or inherit requires_model_validation=True:
cleanup, dumpdata, flush, loaddata, reset, runfcgi, sql, sqlall, sqlclear, sqlcustom, sqlflush, sqlindexes, sqlinitialdata, sqlreset, sqlsequencereset, syncdb, createsuperusers, ping_google, collectstatic, findstatic.

This could also cause hard to diagnose problems e.g. when performing reverse URL resolving.

Backport of [16493] from trunk.

comment:12 Changed 3 years ago by aaugustin

#16755 was a duplicate.

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.