test the meta uniquetogether normalization with different sorts of objects regression test for 12560 regression test for 12560 regression test for 12132 should work without errors should work without errors uniquefordateyearmonth checks shouldn t trigger when the associated datefield is none test the meta unique_together normalization with different sorts of objects ref 21469 regression test for 12560 regression test for 12560 regression test for 12132 should work without errors should work without errors unique_for_date year month checks shouldn t trigger when the associated datefield is none
import datetime
import unittest

from django.apps.registry import Apps
from django.core.exceptions import ValidationError
from django.db import models
from django.test import TestCase

from .models import (
    CustomPKModel,
    FlexibleDatePost,
    ModelToValidate,
    Post,
    UniqueErrorsModel,
    UniqueFieldsModel,
    UniqueForDateModel,
    UniqueFuncConstraintModel,
    UniqueTogetherModel,
)


class GetUniqueCheckTests(unittest.TestCase):
    def test_unique_fields_get_collected(self):
        m = UniqueFieldsModel()
        self.assertEqual(
            (
                [
                    (UniqueFieldsModel, ("id",)),
                    (UniqueFieldsModel, ("unique_charfield",)),
                    (UniqueFieldsModel, ("unique_integerfield",)),
                ],
                [],
            ),
            m._get_unique_checks(),
        )

    def test_unique_together_gets_picked_up_and_converted_to_tuple(self):
        m = UniqueTogetherModel()
        self.assertEqual(
            (
                [
                    (UniqueTogetherModel, ("ifield", "cfield")),
                    (UniqueTogetherModel, ("ifield", "efield")),
                    (UniqueTogetherModel, ("id",)),
                ],
                [],
            ),
            m._get_unique_checks(),
        )

    def test_unique_together_normalization(self):
        
        data = {
            "2-tuple": (("foo", "bar"), (("foo", "bar"),)),
            "list": (["foo", "bar"], (("foo", "bar"),)),
            "already normalized": (
                (("foo", "bar"), ("bar", "baz")),
                (("foo", "bar"), ("bar", "baz")),
            ),
            "set": (
                {("foo", "bar"), ("bar", "baz")},  
                (("foo", "bar"), ("bar", "baz")),
            ),
        }

        for unique_together, normalized in data.values():

            class M(models.Model):
                foo = models.IntegerField()
                bar = models.IntegerField()
                baz = models.IntegerField()

                Meta = type(
                    "Meta", (), {"unique_together": unique_together, "apps": Apps()}
                )

            checks, _ = M()._get_unique_checks()
            for t in normalized:
                check = (M, t)
                self.assertIn(check, checks)

    def test_primary_key_is_considered_unique(self):
        m = CustomPKModel()
        self.assertEqual(
            ([(CustomPKModel, ("my_pk_field",))], []), m._get_unique_checks()
        )

    def test_unique_for_date_gets_picked_up(self):
        m = UniqueForDateModel()
        self.assertEqual(
            (
                [(UniqueForDateModel, ("id",))],
                [
                    (UniqueForDateModel, "date", "count", "start_date"),
                    (UniqueForDateModel, "year", "count", "end_date"),
                    (UniqueForDateModel, "month", "order", "end_date"),
                ],
            ),
            m._get_unique_checks(),
        )

    def test_unique_for_date_exclusion(self):
        m = UniqueForDateModel()
        self.assertEqual(
            (
                [(UniqueForDateModel, ("id",))],
                [
                    (UniqueForDateModel, "year", "count", "end_date"),
                    (UniqueForDateModel, "month", "order", "end_date"),
                ],
            ),
            m._get_unique_checks(exclude="start_date"),
        )

    def test_func_unique_constraint_ignored(self):
        m = UniqueFuncConstraintModel()
        self.assertEqual(
            m._get_unique_checks(),
            ([(UniqueFuncConstraintModel, ("id",))], []),
        )


class PerformUniqueChecksTest(TestCase):
    def test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified(
        self,
    ):
        
        with self.assertNumQueries(0):
            mtv = ModelToValidate(number=10, name="Some Name")
            setattr(mtv, "_adding", True)
            mtv.full_clean()

    def test_primary_key_unique_check_performed_when_adding_and_pk_specified(self):
        
        with self.assertNumQueries(1):
            mtv = ModelToValidate(number=10, name="Some Name", id=123)
            setattr(mtv, "_adding", True)
            mtv.full_clean()

    def test_primary_key_unique_check_not_performed_when_not_adding(self):
        
        with self.assertNumQueries(0):
            mtv = ModelToValidate(number=10, name="Some Name")
            mtv.full_clean()

    def test_unique_for_date(self):
        Post.objects.create(
            title="Django 1.0 is released",
            slug="Django 1.0",
            subtitle="Finally",
            posted=datetime.date(2008, 9, 3),
        )
        p = Post(title="Django 1.0 is released", posted=datetime.date(2008, 9, 3))
        with self.assertRaises(ValidationError) as cm:
            p.full_clean()
        self.assertEqual(
            cm.exception.message_dict,
            {"title": ["Title must be unique for Posted date."]},
        )

        
        p = Post(title="Work on Django 1.1 begins", posted=datetime.date(2008, 9, 3))
        p.full_clean()

        
        p = Post(title="Django 1.0 is released", posted=datetime.datetime(2008, 9, 4))
        p.full_clean()

        p = Post(slug="Django 1.0", posted=datetime.datetime(2008, 1, 1))
        with self.assertRaises(ValidationError) as cm:
            p.full_clean()
        self.assertEqual(
            cm.exception.message_dict,
            {"slug": ["Slug must be unique for Posted year."]},
        )

        p = Post(subtitle="Finally", posted=datetime.datetime(2008, 9, 30))
        with self.assertRaises(ValidationError) as cm:
            p.full_clean()
        self.assertEqual(
            cm.exception.message_dict,
            {"subtitle": ["Subtitle must be unique for Posted month."]},
        )

        p = Post(title="Django 1.0 is released")
        with self.assertRaises(ValidationError) as cm:
            p.full_clean()
        self.assertEqual(
            cm.exception.message_dict, {"posted": ["This field cannot be null."]}
        )

    def test_unique_for_date_with_nullable_date(self):
        
        FlexibleDatePost.objects.create(
            title="Django 1.0 is released",
            slug="Django 1.0",
            subtitle="Finally",
            posted=datetime.date(2008, 9, 3),
        )
        p = FlexibleDatePost(title="Django 1.0 is released")
        p.full_clean()

        p = FlexibleDatePost(slug="Django 1.0")
        p.full_clean()

        p = FlexibleDatePost(subtitle="Finally")
        p.full_clean()

    def test_unique_errors(self):
        UniqueErrorsModel.objects.create(name="Some Name", no=10)
        m = UniqueErrorsModel(name="Some Name", no=11)
        with self.assertRaises(ValidationError) as cm:
            m.full_clean()
        self.assertEqual(
            cm.exception.message_dict, {"name": ["Custom unique name message."]}
        )

        m = UniqueErrorsModel(name="Some Other Name", no=10)
        with self.assertRaises(ValidationError) as cm:
            m.full_clean()
        self.assertEqual(
            cm.exception.message_dict, {"no": ["Custom unique number message."]}
        )