From 108b8bf852c76855ed98f5abe55db1da845598e7 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Wed, 17 Dec 2014 17:25:28 +0100 Subject: [PATCH] Fixed #24007 -- Ensure apps registry's ready before unpickling models This prevents AppRegistryNotReady errors when unpickling Django models from an external script. --- django/db/models/base.py | 2 ++ docs/releases/1.7.2.txt | 4 ++++ tests/model_regress/test_pickle.py | 35 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index af007d77ce..d0c1b7ad16 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1658,6 +1658,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 88f3d3e1d8..47a56084d0 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/test_pickle.py b/tests/model_regress/test_pickle.py index 8dc90893bf..ab70b4ec91 100644 --- a/tests/model_regress/test_pickle.py +++ b/tests/model_regress/test_pickle.py @@ -1,4 +1,7 @@ +import datetime import pickle +import subprocess +import tempfile import warnings from django.db import models, DJANGO_VERSION_PICKLE_KEY @@ -6,6 +9,8 @@ from django.test import TestCase from django.utils.encoding import force_text from django.utils.version import get_major_version, get_version +from .models import Article + class ModelPickleTestCase(TestCase): def test_missing_django_version_unpickling(self): @@ -51,3 +56,33 @@ class ModelPickleTestCase(TestCase): "Pickled model instance's Django version %s does not " "match the current version %s." % (str(float(get_major_version()) - 0.1), get_version())) + + 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")