From 4f7467b6905482a5d826c2815dcf8c6dd332340d Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 3 Apr 2018 11:12:56 -0400 Subject: [PATCH] Refs #28577 -- Added check for HStoreField to prevent mutable default. --- django/contrib/postgres/fields/hstore.py | 5 +++- tests/postgres_tests/test_hstore.py | 34 +++++++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/django/contrib/postgres/fields/hstore.py b/django/contrib/postgres/fields/hstore.py index d47b34d459..39f074b763 100644 --- a/django/contrib/postgres/fields/hstore.py +++ b/django/contrib/postgres/fields/hstore.py @@ -6,15 +6,18 @@ from django.core import exceptions from django.db.models import Field, TextField, Transform from django.utils.translation import gettext_lazy as _ +from .mixins import CheckFieldDefaultMixin + __all__ = ['HStoreField'] -class HStoreField(Field): +class HStoreField(CheckFieldDefaultMixin, Field): empty_strings_allowed = False description = _('Map of strings to strings/nulls') default_error_messages = { 'not_a_string': _('The value of "%(key)s" is not a string or null.'), } + _default_hint = ('dict', '{}') def db_type(self, connection): return 'hstore' diff --git a/tests/postgres_tests/test_hstore.py b/tests/postgres_tests/test_hstore.py index b58e5e5e20..a51cb4e66f 100644 --- a/tests/postgres_tests/test_hstore.py +++ b/tests/postgres_tests/test_hstore.py @@ -1,11 +1,11 @@ import json -from django.core import exceptions, serializers +from django.core import checks, exceptions, serializers from django.forms import Form -from django.test.utils import modify_settings +from django.test.utils import isolate_apps, modify_settings from . import PostgreSQLTestCase -from .models import HStoreModel +from .models import HStoreModel, PostgreSQLModel try: from django.contrib.postgres import forms @@ -190,6 +190,34 @@ class TestQuerying(HStoreTestCase): ) +@isolate_apps('postgres_tests') +class TestChecks(PostgreSQLTestCase): + + def test_invalid_default(self): + class MyModel(PostgreSQLModel): + field = HStoreField(default={}) + + model = MyModel() + self.assertEqual(model.check(), [ + checks.Warning( + msg=( + "HStoreField default should be a callable instead of an " + "instance so that it's not shared between all field " + "instances." + ), + hint='Use a callable instead, e.g., use `dict` instead of `{}`.', + obj=MyModel._meta.get_field('field'), + id='postgres.E003', + ) + ]) + + def test_valid_default(self): + class MyModel(PostgreSQLModel): + field = HStoreField(default=dict) + + self.assertEqual(MyModel().check(), []) + + class TestSerialization(HStoreTestCase): test_data = json.dumps([{ 'model': 'postgres_tests.hstoremodel',