#31357: Stale content types can cause exception in ContentType.get_for_models
------------------------------------------------+------------------------
               Reporter:  Andy Chosak           |          Owner:  nobody
                   Type:  Bug                   |         Status:  new
              Component:  contrib.contenttypes  |        Version:  3.0
               Severity:  Normal                |       Keywords:
           Triage Stage:  Unreviewed            |      Has patch:  0
    Needs documentation:  0                     |    Needs tests:  0
Patch needs improvement:  0                     |  Easy pickings:  0
                  UI/UX:  0                     |
------------------------------------------------+------------------------
 `ContentType.get_for_models`
 
([[https://github.com/django/django/blob/e3e48b00127c09eafe6439d980a82fc5c591b673/django/contrib/contenttypes/models.py#L62|source]])
 retrieves content types from the database for a given list of models. Due
 to the way this method works, it's possible to trigger an unexpected
 `AttributeError` by passing in a list of models whose `(app_label,
 model_name)` pairs can be permuted to refer to a model with a stale
 content type in the database.

 Consider this example:

 1. A project has an app named `foo` with models named `Author` and `Book`.
 Content types for these exist in the database.
 2. A new app named `bar` is created providing an alternate `Book` model.
 The content type for this also exists.
 3. The model `foo.Book` is deleted. The database is migrated, but the
 stale content type is left behind.

 The following code will raise an exception:

 {{{
 >> from foo.models import Author
 >>> from bar.models import Book
 >>> from django.contrib.contenttypes.models import ContentType
 >>> ContentType.objects.get_for_models(Author, Book)
 Traceback (most recent call last):
   File "<console>", line 1, in <module>
   File "/path/to/myproject/lib/python3.6/site-
 packages/django/contrib/contenttypes/models.py", line 89, in
 get_for_models
     opts_models = needed_opts.pop(ct.model_class()._meta, [])
 AttributeError: 'NoneType' object has no attribute '_meta'
 }}}

 This occurs because the `ct.model_class()` call returns `None` for stale
 content types.

 The problem is in
 
[[https://github.com/django/django/blob/e3e48b00127c09eafe6439d980a82fc5c591b673/django/contrib/contenttypes/models.py#L83-L87|this
 database lookup]] that tries to pull back content types for the models
 that were passed in. Instead of pulling back only the `ContentType`
 instances for those models, it filters by
 `app_label__in=needed_app_labels, model__in=needed_models`. For the above
 case, this pulls back not just `(foo, Author)` and `(bar, Book)` but also
 `(foo, Book)`, which in this case is stale.

 A fix here could either alter the query to be more specific or check the
 returned content types to make sure they correspond to one of the desired
 models.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/31357>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/049.3305fe724694aecb20bfedd09d6af4b0%40djangoproject.com.

Reply via email to