Opened 6 years ago

Closed 6 years ago

#29051 closed Bug (worksforme)

unique_together behavior undocumented changes in 1.11

Reported by: Christian Pedersen Owned by: nobody
Component: Documentation Version: 1.11
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In Django 1.10 unique_together would take complain even if a field was null.
In 1.11 this changed so that it uses the database style, in this case Postgres.
The only thing in the release notes is:
On Oracle, Model.validate_unique() no longer checks empty strings for uniqueness as the database interprets the value as NULL.
no mention of Postgres.

Change History (4)

comment:1 by Tim Graham, 6 years ago

I'm not sure what you mean -- can you give specific steps to reproduce the problem? The release note you mentioned is from 267dc4adddd2882182f71a7f285a06b1d4b15af0. Is it that same commit that's causing your problem?

in reply to:  1 comment:2 by Christian Pedersen, 6 years ago

Replying to Tim Graham:

I'm not sure what you mean -- can you give specific steps to reproduce the problem? The release note you mentioned is from 267dc4adddd2882182f71a7f285a06b1d4b15af0. Is it that same commit that's causing your problem?

Of course. Give this simplified model:

class MyModel(models.Model):
    field1 = models.ForeignKey(FModel, on_delete=models.CASCADE)
    field2 = models.CharField()
    field3 = models.CharField(blank=True, null=True)

    class Meta:
        unique_together = (("field1", "field2", "field3"),)

It is possible to do:

MyModel.objects.create(field1=ref, field2='Name', field3=None)
MyModel.objects.create(field1=ref, field2='Name', field3=None)

and it will insert this model twice. In Django 1.10 this wouldn't happen, as Django would check all three fields. However in 1.11 it seems to emulate how PostgreSQL does it.
My problem with this is that it's an undocumented change and the only other mention of it I could find was marked as "wontfix".

I believe the commit you mention is the one that changes this behavior. Probably these lines: https://github.com/django/django/commit/267dc4adddd2882182f71a7f285a06b1d4b15af0#diff-bf776a3b8e5dbfac2432015825ef8afeR1090

comment:3 by Tim Graham, 6 years ago

I don't think the code you gave reproduces the issue. It works on all versions of Django that I tested. I imagine the correct steps to reproduce involve a model form.

The release note for 1.11 says, "In model forms, CharField with null=True now saves NULL for blank values instead of empty strings."

The documentation for Field.null says, "In most cases, it’s redundant to have two possible values for “no data;” the Django convention is to use the empty string, not NULL. One exception is when a CharField has both unique=True and blank=True set. In this situation, null=True is required to avoid unique constraint violations when saving multiple objects with blank values."

The new behavior you mentioned seems to follow naturally if you understand what's happening (for the purposes of a unique constraint, PostgreSQL and other databases don't consider null values equal to each other). I'm not sure that a clarification of the release notes is required but do you want to propose one?

comment:4 by Tim Graham, 6 years ago

Component: UncategorizedDocumentation
Resolution: worksforme
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top