From 934a16dc93a5f46f965ba299dbd543703d32d493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20=C5=9Awiat?= Date: Thu, 30 Oct 2014 18:38:47 +0100 Subject: [PATCH] Fixed #23468 -- Added checks for duplicate fixtures directories in loaddata. If settings.FIXTURE_DIRS contains duplicates or a default fixture directory (app_name/fixtures), ImproperlyConfigured is raised. Thanks to Berker Peksag and Tim Graham for review. --- django/core/management/commands/loaddata.py | 17 ++++++-- docs/releases/1.8.txt | 4 ++ tests/fixtures_regress/tests.py | 43 +++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 05cf769489d..aae69afc9b5 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -9,6 +9,7 @@ import zipfile from django.apps import apps from django.conf import settings from django.core import serializers +from django.core.exceptions import ImproperlyConfigured from django.core.management.base import BaseCommand, CommandError from django.core.management.color import no_style from django.db import (connections, router, transaction, DEFAULT_DB_ALIAS, @@ -238,13 +239,23 @@ class Command(BaseCommand): current directory. """ dirs = [] + fixture_dirs = settings.FIXTURE_DIRS + if len(fixture_dirs) != len(set(fixture_dirs)): + raise ImproperlyConfigured("settings.FIXTURE_DIRS contains duplicates.") for app_config in apps.get_app_configs(): - if self.app_label and app_config.label != self.app_label: - continue + app_label = app_config.label app_dir = os.path.join(app_config.path, 'fixtures') + if app_dir in fixture_dirs: + raise ImproperlyConfigured( + "'%s' is a default fixture directory for the '%s' app " + "and cannot be listed in settings.FIXTURE_DIRS." % (app_dir, app_label) + ) + + if self.app_label and app_label != self.app_label: + continue if os.path.isdir(app_dir): dirs.append(app_dir) - dirs.extend(list(settings.FIXTURE_DIRS)) + dirs.extend(list(fixture_dirs)) dirs.append('') dirs = [upath(os.path.abspath(os.path.realpath(d))) for d in dirs] return dirs diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index d02672be9ab..f84294d4592 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -284,6 +284,10 @@ Management Commands * The :djadminopt:`--name` option for :djadmin:`makemigrations` allows you to to give the migration(s) a custom name instead of a generated one. +* The :djadmin:`loaddata` command now prevents repeated fixture loading. If + :setting:`FIXTURE_DIRS` contains duplicates or a default fixture directory + path (``app_name/fixtures``), an exception is raised. + Migrations ^^^^^^^^^^ diff --git a/tests/fixtures_regress/tests.py b/tests/fixtures_regress/tests.py index 0ec48bc5e95..171ef30fecb 100644 --- a/tests/fixtures_regress/tests.py +++ b/tests/fixtures_regress/tests.py @@ -8,6 +8,7 @@ import re import warnings from django.core import serializers +from django.core.exceptions import ImproperlyConfigured from django.core.serializers.base import DeserializationError from django.core import management from django.core.management.base import CommandError @@ -486,6 +487,48 @@ class TestFixtures(TestCase): verbosity=0, ) + @override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures_1'), + os.path.join(_cur_dir, 'fixtures_1')]) + def test_fixture_dirs_with_duplicates(self): + """ + settings.FIXTURE_DIRS cannot contain duplicates in order to avoid + repeated fixture loading. + """ + self.assertRaisesMessage( + ImproperlyConfigured, + "settings.FIXTURE_DIRS contains duplicates.", + management.call_command, + 'loaddata', + 'absolute.json', + verbosity=0, + ) + + @override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures')]) + def test_fixture_dirs_with_default_fixture_path(self): + """ + settings.FIXTURE_DIRS cannot contain a default fixtures directory + for application (app/fixtures) in order to avoid repeated fixture loading. + """ + self.assertRaisesMessage( + ImproperlyConfigured, + "'%s' is a default fixture directory for the '%s' app " + "and cannot be listed in settings.FIXTURE_DIRS." + % (os.path.join(_cur_dir, 'fixtures'), 'fixtures_regress'), + management.call_command, + 'loaddata', + 'absolute.json', + verbosity=0, + ) + + @override_settings(FIXTURE_DIRS=[os.path.join(_cur_dir, 'fixtures_1'), + os.path.join(_cur_dir, 'fixtures_2')]) + def test_loaddata_with_valid_fixture_dirs(self): + management.call_command( + 'loaddata', + 'absolute.json', + verbosity=0, + ) + class NaturalKeyFixtureTests(TestCase):