Commit cb152318 authored by Daniel Pyrathon's avatar Daniel Pyrathon Committed by Tim Graham
Browse files

Fixed #21798 -- Added check for DateTime mutually exclusive options

Added DateTimeCheckMixin to avoid the use of default, auto_now, and
auto_now_add options together. Added the fields.E151 Error that is raised
if one or more of these options are used together.
parent 8a9d54aa
Loading
Loading
Loading
Loading
+32 −2
Original line number Diff line number Diff line
@@ -1074,7 +1074,37 @@ class CommaSeparatedIntegerField(CharField):
        return super(CommaSeparatedIntegerField, self).formfield(**defaults)


class DateField(Field):
class DateTimeCheckMixin(object):

    def check(self, **kwargs):
        errors = super(DateTimeCheckMixin, self).check(**kwargs)
        errors.extend(self._check_mutually_exclusive_options())
        return errors

    def _check_mutually_exclusive_options(self):
        # auto_now, auto_now_add, and default are mutually exclusive
        # options. The use of more than one of these options together
        # will trigger an Error
        mutually_exclusive_options = [self.auto_now_add, self.auto_now,
                                      self.has_default()]
        enabled_options = [option not in (None, False)
                          for option in mutually_exclusive_options].count(True)
        if enabled_options > 1:
            return [
                checks.Error(
                    "The options auto_now, auto_now_add, and default "
                    "are mutually exclusive. Only one of these options "
                    "may be present.",
                    hint=None,
                    obj=self,
                    id='fields.E151',
                )
            ]
        else:
            return []


class DateField(DateTimeCheckMixin, Field):
    empty_strings_allowed = False
    default_error_messages = {
        'invalid': _("'%(value)s' value has an invalid date format. It must be "
@@ -1887,7 +1917,7 @@ class TextField(Field):
        return super(TextField, self).formfield(**defaults)


class TimeField(Field):
class TimeField(DateTimeCheckMixin, Field):
    empty_strings_allowed = False
    default_error_messages = {
        'invalid': _("'%(value)s' value has an invalid format. It must be in "
+1 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ Fields
* **fields.E134**: ``max_digits`` must be greater or equal to ``decimal_places``.
* **fields.E140**: FilePathFields must have either ``allow_files`` or ``allow_folders`` set to True.
* **fields.E150**: GenericIPAddressFields cannot accept blank values if null values are not allowed, as blank values are stored as nulls.
* **fields.E151**: The options ``auto_now``, ``auto_now_add``, and ``default`` are mutually exclusive. Only one of these options may be present.

File Fields
~~~~~~~~~~~
+3 −0
Original line number Diff line number Diff line
@@ -477,6 +477,9 @@ The default form widget for this field is a
and a shortcut for "Today". Includes an additional ``invalid_date`` error
message key.

The options ``auto_now_add``, ``auto_now``, and ``default`` are mutually exclusive.
Any combination of these options will result in an error.

.. note::
    As currently implemented, setting ``auto_now`` or ``auto_now_add`` to
    ``True`` will cause the field to have ``editable=False`` and ``blank=True``
+28 −0
Original line number Diff line number Diff line
# -*- encoding: utf-8 -*-
from __future__ import unicode_literals

from datetime import datetime
import unittest

from django.core.checks import Error
@@ -399,3 +400,30 @@ class ImageFieldTests(IsolatedModelsTestCase):
            ),
        ]
        self.assertEqual(errors, expected)


class DateFieldTests(IsolatedModelsTestCase):

    def test_auto_now_and_auto_now_add_raise_error(self):
            dn = datetime.now
            mutually_exclusive_combinations = (
                (True, True, dn),
                (True, False, dn),
                (False, True, dn),
                (True, True, None)
            )

            for auto_now, auto_now_add, default in mutually_exclusive_combinations:
                field = models.DateTimeField(name="field", auto_now=auto_now,
                                             auto_now_add=auto_now_add,
                                             default=default)
                expected = [Error(
                    "The options auto_now, auto_now_add, and default "
                    "are mutually exclusive. Only one of these options "
                    "may be present.",
                    hint=None,
                    obj=field,
                    id='fields.E151',
                )]
                checks = field.check()
                self.assertEqual(checks, expected)