Opened 9 years ago

Closed 9 years ago

Last modified 9 years ago

#26105 closed Bug (duplicate)

create_permissions uses ContentType cache which contains non-existing pks

Reported by: Sven R. Kunze Owned by: nobody
Component: Database layer (models, ORM) Version: 1.8
Severity: Normal Keywords:
Cc: tzanke@… Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

While migrating a larger project, we found the traceback attached.

We found that create_permissions from django/contrib/auth/management/__init__.py uses the ContentType.objects._cache instead of querying the database for the real pk of the contenttype.

Real pks from database:

 id  |       app_label       |                   model                    
-----+-----------------------+--------------------------------------------
   1 | auth                  | permission
   2 | auth                  | group
   3 | auth                  | user

Pks generated by django (execution at breakpoint at line 103):

>>> [(id, ct) for id, ct in ContentType.objects._cache['default'].items() if ct.app_label == 'auth']
[
    (2, <ContentType: group>),
    (3, <ContentType: permission>),
    (4, <ContentType: user>),
]

It seems Django confused real pks with imaginary pks. We created a placeholder contenttype with pk=4 before rerunning the migrations, which fixed the exception but created a new permission linked to this "false" contenttype.

If the contenttype with pk=4 were a real one (i.e. an existing model etc.), a wrong permission would have been created then.

=> select * from django_content_type where id=4;
 id | app_label |     model     
----+-----------+---------------
  4 | auth      | XXXXXXXXXXXXX

=> select * from auth_permission where content_type_id=4;
 id  |      name       | content_type_id |  codename   
-----+-----------------+-----------------+-------------
 598 | Can delete user |               4 | delete_user
 597 | Can change user |               4 | change_user
 596 | Can add user    |               4 | add_user

The migration traceback:

Traceback (most recent call last):
  File "lib/python2.7/site-packages/django/core/management/__init__.py", line 354, in execute_from_command_line
    utility.execute()
  File "lib/python2.7/site-packages/django/core/management/__init__.py", line 346, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "lib/python2.7/site-packages/django/core/management/base.py", line 394, in run_from_argv
    self.execute(*args, **cmd_options)
  File "lib/python2.7/site-packages/django/core/management/base.py", line 445, in execute
    output = self.handle(*args, **options)
  File "lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 226, in handle
    emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias)
  File "lib/python2.7/site-packages/django/core/management/sql.py", line 280, in emit_post_migrate_signal
    using=db)
  File "lib/python2.7/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
  File "lib/python2.7/site-packages/django/contrib/auth/management/__init__.py", line 114, in create_permissions 
    Permission.objects.using(using).bulk_create(perms)
  File "lib/python2.7/site-packages/django/db/models/query.py", line 392, in bulk_create
    self._batched_insert(objs_without_pk, fields, batch_size)
  File "lib/python2.7/site-packages/django/db/transaction.py", line 225, in __exit__
    connection.commit() 
  File "lib/python2.7/site-packages/django/db/backends/base/base.py", line 173, in commit
    self._commit() 
  File "lib/python2.7/site-packages/django/db/backends/base/base.py", line 142, in _commit
    return self.connection.commit()
  File "lib/python2.7/site-packages/django/db/utils.py", line 98, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "lib/python2.7/site-packages/django/db/backends/base/base.py", line 142, in _commit
    return self.connection.commit()
django.db.utils.IntegrityError: insert or update on table "auth_permission" violates foreign key constraint "auth_content_type_id_508cf46651277a81_fk_django_content_type_id"
DETAIL:  Key (content_type_id)=(4) is not present in table "django_content_type".

Change History (4)

comment:1 by Sven R. Kunze, 9 years ago

A quick fix we found could be (line 90 to line 100):

all_perms=set(Permission.objects.using(using).filter(
    content_type__in=ctypes,
).values_list(
    "content_type__app_label", "content_type__model", "codename"
))


perms =[
    Permission(codename=codename, name=name, content_type=ContentType.objects.get(app_label=ct.app_label, model=ct.model))
    for ct, (codename, name) in searched_perms
    if (ct.app_label, ct.model, codename) not in all_perms
]
Version 1, edited 9 years ago by Sven R. Kunze (previous) (next) (diff)

comment:2 by Tim Graham, 9 years ago

Might be a duplicate of #10827. Could you test the fix proposed on that ticket? We're still waiting for a test for that patch if you're able to provide one.

comment:3 by Sven R. Kunze, 9 years ago

Resolution: duplicate
Status: newclosed

It seems it is.

comment:4 by TZanke, 9 years ago

Cc: tzanke@… added
Note: See TracTickets for help on using tickets.
Back to Top