Opened 9 years ago

Last modified 3 years ago

#26401 new New feature

Allow auth machinery to be used without installing auth app

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

Description

Django projects should be able to use the auth machinery (like django.contrib.auth.middleware.AuthenticationMiddleware and django.contrib.auth.context_processors.auth) without having django.contrib.auth in INSTALLED_APPS

See

https://groups.google.com/forum/#!topic/django-developers/2DzLBbk8w-w

This ticket is for resolving the current issue in 1.9. I also want to write a test or two to prevent this issue from happening in the future.

Attachments (1)

ticket-26401-remove-stale-contenttypes.patch (2.9 KB ) - added by Jon Dufresne 8 years ago.
Test case for remove_stale_contenttypes with auth migrations disabled

Download all attachments as: .zip

Change History (29)

comment:1 by Matt Johnson, 9 years ago

Owner: changed from nobody to Matt Johnson
Status: newassigned

comment:2 by Florian Apolloner, 9 years ago

Triage Stage: UnreviewedAccepted

comment:4 by Tim Graham, 9 years ago

Needs tests: set

comment:5 by Carl Meyer, 8 years ago

Owner: Matt Johnson removed
Status: assignednew

De-assigning so it's clear this is available for a PyCon sprinter to finish up with tests.

comment:6 by Andrew Konoff, 8 years ago

Owner: set to Andrew Konoff
Status: newassigned

comment:7 by Matt Johnson, 8 years ago

comment:8 by Aymeric Augustin, 8 years ago

FWIW I don't see a lot of value in adding this constraint. Implementing it requires a fragile and untestable combination of inner imports to avoid touching the models module.

If the only problem is to avoid creating tables in the database, add a MIGRATION_MODULES entry for the auth app that points to an empty package. I never tried it but I think it should work.

If this workaround is confirmed to work, I propose to close this ticket as wontfix.

comment:9 by Matt Johnson, 8 years ago

Interesting idea. It works:

MIGRATION_MODULES = {
    'auth': None
}

My complaint was driven by the unnecessary clutter in my database when using a custom user model. So now that there is a reasonable solution, I'm fine with closing this.

comment:10 by Matt Johnson, 8 years ago

Resolution: wontfix
Status: assignedclosed

comment:11 by Aymeric Augustin, 8 years ago

Great! Thanks for reporting back.

comment:12 by Jon Dufresne, 8 years ago

I found the above to be insufficient. Adding MIGRATION_MODULES as the only change to avoid the auth tables fails during migrations with the following trace:

Traceback (most recent call last):
  File ".../manage.py", line 9, in <module>
    execute_from_command_line(sys.argv)
  File ".../venv/lib64/python3.5/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File ".../venv/lib64/python3.5/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File ".../venv/lib64/python3.5/site-packages/django/core/management/base.py", line 305, in run_from_argv
    self.execute(*args, **cmd_options)
  File ".../venv/lib64/python3.5/site-packages/django/core/management/base.py", line 356, in execute
    output = self.handle(*args, **options)
  File ".../venv/lib64/python3.5/site-packages/django/core/management/commands/migrate.py", line 222, in handle
    self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan,
  File ".../venv/lib64/python3.5/site-packages/django/core/management/sql.py", line 53, in emit_post_migrate_signal
    **kwargs
  File ".../venv/lib64/python3.5/site-packages/django/dispatch/dispatcher.py", line 191, in send
    response = receiver(signal=self, sender=sender, **named)
  File ".../venv/lib64/python3.5/site-packages/django/contrib/auth/management/__init__.py", line 75, in create_permissions
    "content_type", "codename"
  File ".../venv/lib64/python3.5/site-packages/django/db/models/query.py", line 256, in __iter__
    self._fetch_all()
  File ".../venv/lib64/python3.5/site-packages/django/db/models/query.py", line 1085, in _fetch_all
    self._result_cache = list(self.iterator())
  File ".../venv/lib64/python3.5/site-packages/django/db/models/query.py", line 125, in __iter__
    for row in compiler.results_iter():
  File ".../venv/lib64/python3.5/site-packages/django/db/models/sql/compiler.py", line 789, in results_iter
    results = self.execute_sql(MULTI)
  File ".../venv/lib64/python3.5/site-packages/django/db/models/sql/compiler.py", line 835, in execute_sql
    cursor.execute(sql, params)
  File ".../venv/lib64/python3.5/site-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File ".../venv/lib64/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File ".../venv/lib64/python3.5/site-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File ".../venv/lib64/python3.5/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File ".../venv/lib64/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "auth_permission" does not exist
LINE 1: ...ntent_type_id", "auth_permission"."codename" FROM "auth_perm...

This fails becuase the auth's AppConfig adds a post_migrate callback that requires the auth_permission table to exist. A project could avoid this by overriding the auth's AppConfig. I think this could be easier for projects if the auth's AppConfig was split into two classes, one base version that can be used without the auth tables and one that matches the current implementation. This would be a backwards compatible change that would allow the MIGRATION_MODULES change noted above. For example:

class BaseAuthConfig(AppConfig):
    name = 'django.contrib.auth'
    verbose_name = _("Authentication and Authorization")

    def ready(self):
        checks.register(check_user_model, checks.Tags.models)

class AuthConfig(BaseAuthConfig):
    def ready(self):
        post_migrate.connect(
            create_permissions,
            dispatch_uid="django.contrib.auth.management.create_permissions"
        )
        checks.register(check_models_permissions, checks.Tags.models)

comment:13 by Claude Paroz, 8 years ago

Resolution: wontfix
Status: closednew

comment:14 by Jon Dufresne, 8 years ago

PR implementing the AppConfigs described above.

Not sure how to best add a test for this though. Looking for advice. Details in the PR.

comment:15 by Jon Dufresne, 8 years ago

Needs tests: unset

A test has been added to the PR above.

comment:16 by Tim Graham, 8 years ago

Easy pickings: unset
Patch needs improvement: set

comment:17 by Tim Graham, 8 years ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:18 by GitHub <noreply@…>, 8 years ago

Resolution: fixed
Status: newclosed

In 1ec1633c:

Fixed #26401 -- Added BaseAuthConfig to use auth without migrations.

comment:19 by Jon Dufresne, 8 years ago

I've hit another snag with the MIGRATION_MODULES = {'auth': None} approach. (Sorry, is it better to reopen this ticket or file a new one?)

The auth.Permission model has a fk on ContentType. Due to this, when deleting a ContentType (such as with the remove_stale_contenttypes command) queries against the missing auth_permission table can occur.

Not sure how best to handle this scenario. IIUC, as long as auth is in INSTALLED_APPS, the models will be loaded and this FK will cause an issue.

Here is a stack trace when deleting a stale content type

Traceback (most recent call last):
  File "/usr/lib64/python3.5/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib64/python3.5/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/jon/devel/django/django/__main__.py", line 9, in <module>
    management.execute_from_command_line()
  File "/home/jon/devel/django/django/core/management/__init__.py", line 366, in execute_from_command_line
    utility.execute()
  File "/home/jon/devel/django/django/core/management/__init__.py", line 358, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/jon/devel/django/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/jon/devel/django/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/home/jon/devel/django/django/contrib/contenttypes/management/commands/remove_stale_contenttypes.py", line 46, in handle
    collector.collect([ct])
  File "/home/jon/devel/django/django/db/models/deletion.py", line 218, in collect
    elif sub_objs:
  File "/home/jon/devel/django/django/db/models/query.py", line 260, in __bool__
    self._fetch_all()
  File "/home/jon/devel/django/django/db/models/query.py", line 1072, in _fetch_all
    self._result_cache = list(self.iterator())
  File "/home/jon/devel/django/django/db/models/query.py", line 54, in __iter__
    results = compiler.execute_sql()
  File "/home/jon/devel/django/django/db/models/sql/compiler.py", line 847, in execute_sql
    cursor.execute(sql, params)
  File "/home/jon/devel/django/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/home/jon/devel/django/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/home/jon/devel/django/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/home/jon/devel/django/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/home/jon/devel/django/django/db/backends/sqlite3/base.py", line 334, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: auth_permission

by Jon Dufresne, 8 years ago

Test case for remove_stale_contenttypes with auth migrations disabled

comment:20 by Tim Graham, 8 years ago

Has patch: unset
Keywords: 1.11 added
Triage Stage: Ready for checkinAccepted
Type: BugNew feature

It's appropriate to reopen a ticket if the fix hasn't been released yet.

comment:21 by Jon Dufresne, 8 years ago

Resolution: fixed
Status: closednew

Reopening until the above issue is handled.

comment:22 by Jon Dufresne, 8 years ago

Has patch: set

I'm unable to think of a sensible solution to the ContentType auth.Permission dependency issue. Therefore, I'm suggesting to revert my previous PR as using auth without migrations will not fully work.

PR to revert

comment:23 by Tim Graham, 8 years ago

It seems like you basically want an apps.register.Apps.unregister_model() method or some way of telling Django not to setup relations for a given model. This might not be trivial, so if you want to revert for now, that's fine with me.

comment:24 by Jon Dufresne <jon.dufresne@…>, 8 years ago

Resolution: fixed
Status: newclosed

In f3ea0c4:

Reverted "Fixed #26401 -- Added BaseAuthConfig to use auth without migrations."

This reverts commit 1ec1633cb294d8ce2a65ece6b56c258483596fba as it
doesn't handle ContentType's auth.Permission dependency. Thus, it
doesn't allow auth without migrations.

comment:25 by Jon Dufresne, 8 years ago

Resolution: fixed
Status: closednew

Reopening. The revert commit was merged.

comment:26 by Tim Graham, 8 years ago

Has patch: unset
Keywords: 1.11 removed

comment:27 by Mariusz Felisiak, 3 years ago

Owner: Andrew Konoff removed
Status: newassigned

comment:28 by Mariusz Felisiak, 3 years ago

Status: assignednew
Note: See TracTickets for help on using tickets.
Back to Top