From 47912d9f2b8f55044aef5b8ed9b006e8dd8ce976 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 17 Dec 2014 17:25:28 +0100 Subject: [PATCH] [1.7.x] Fixed #24007 -- Ensure apps registry's ready before unpickling models This prevents AppRegistryNotReady errors when unpickling Django models from an external script. Backport of 108b8bf85 from master. --- django/db/models/base.py | 2 ++ docs/releases/1.7.2.txt | 4 ++++ tests/model_regress/tests.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index e802dd955d3..8037764dd82 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1459,6 +1459,8 @@ def model_unpickle(model_id, attrs, factory): Used to unpickle Model subclasses with deferred fields. """ if isinstance(model_id, tuple): + if not apps.ready: + apps.populate(settings.INSTALLED_APPS) model = apps.get_model(*model_id) else: # Backwards compat - the model was cached directly in earlier versions. diff --git a/docs/releases/1.7.2.txt b/docs/releases/1.7.2.txt index 88f3d3e1d88..47a56084d08 100644 --- a/docs/releases/1.7.2.txt +++ b/docs/releases/1.7.2.txt @@ -151,3 +151,7 @@ Bugfixes (:ticket:`23975`). * Made admin system checks run for custom ``AdminSite``\s (:ticket:`23497`). + +* Ensured the app registry is fully populated when unpickling models. When an + external script (like a queueing infrastructure) reloads pickled models, it + could crash with an ``AppRegistryNotReady`` exception (:ticket:`24007`). diff --git a/tests/model_regress/tests.py b/tests/model_regress/tests.py index 057798c785f..f17c134ac96 100644 --- a/tests/model_regress/tests.py +++ b/tests/model_regress/tests.py @@ -2,7 +2,10 @@ from __future__ import unicode_literals import datetime from operator import attrgetter +import pickle +import subprocess import sys +import tempfile import unittest from django.core.exceptions import ValidationError @@ -245,3 +248,35 @@ class EvaluateMethodTest(TestCase): dept = Department.objects.create(pk=1, name='abc') dept.evaluate = 'abc' Worker.objects.filter(department=dept) + + +class ModelPickleTestCase(TestCase): + def test_unpickling_when_appregistrynotready(self): + """ + #24007 -- Verifies that a pickled model can be unpickled without having + to manually setup the apps registry beforehand. + """ + script_template = """#!/usr/bin/env python +import pickle + +from django.conf import settings + +data = %r + +settings.configure(DEBUG=False, INSTALLED_APPS=('model_regress',), SECRET_KEY = "blah") +article = pickle.loads(data) +print(article.headline)""" + a = Article.objects.create( + headline="Some object", + pub_date=datetime.datetime.now(), + article_text="This is an article", + ) + + with tempfile.NamedTemporaryFile(mode='w+', suffix=".py", dir='.', delete=True) as script: + script.write(script_template % pickle.dumps(a)) + script.flush() + try: + result = subprocess.check_output(['python', script.name]) + except subprocess.CalledProcessError: + self.fail("Unable to reload model pickled data") + self.assertEqual(result.strip().decode(), "Some object")