574

Is there a way to define a couple of fields as unique in Django?

I have a table of volumes (of journals) and I don't want more then one volume number for the same journal.

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

I tried to put unique = True as attribute in the fields journal_id and volume_number but it doesn't work.

SO Stinks
  • 3,258
  • 4
  • 32
  • 37
Giovanni Di Milia
  • 13,480
  • 13
  • 55
  • 67

5 Answers5

920

There is a simple solution for you called unique_together which does exactly what you want.

For example:

class MyModel(models.Model):
  field1 = models.CharField(max_length=50)
  field2 = models.CharField(max_length=50)

  class Meta:
    unique_together = ('field1', 'field2',)

And in your case:

class Volume(models.Model):
  id = models.AutoField(primary_key=True)
  journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
  volume_number = models.CharField('Volume Number', max_length=100)
  comments = models.TextField('Comments', max_length=4000, blank=True)

  class Meta:
    unique_together = ('journal_id', 'volume_number',)
Mark Mikofski
  • 19,398
  • 2
  • 57
  • 90
Jens
  • 20,533
  • 11
  • 60
  • 86
  • 1
    How can I check, what exception will be thrown, when I try to create an object that breaks this constraint? – gruszczy Apr 07 '10 at 10:37
  • 2
    I would say you'll get a "ValidationError" exception. Have a look at the Django docs: Model.validate_unique – Jens Apr 10 '10 at 15:09
  • 2
    How would you handle this say if volume_number could be null? Mysql won't seem to enforce unique in that case. – Greg Jun 27 '11 at 17:28
  • 36
    FYI it throws a django.db.utils.IntegrityError if you try to add a duplicate. – araneae Jan 10 '12 at 22:44
  • 9
    @Greg - According to the ANSI standard SQL:2003 (and previous ones as well), a `UNIQUE` constraint should disallow duplicate non-`NULL` values, but allow multiple `NULL` values (see draft http://www.wiscorp.com/sql_2003_standard.zip, Framework, p. 22). If you want your unique constraint to disallow multiple null values, you are probably doing something wrong, like using `NULL` as a meaningfull value. Remember, nullable field says "We don't always have a value for that field but when we do it must be unique.". –  Oct 10 '12 at 07:52
  • 4
    What about multiple `unique_together` constraints? For example - when I want to have mode columns to be unique in the scope of the parent? Well, this property is actually a tuple itself, see: https://docs.djangoproject.com/en/1.4/ref/models/options/#django.db.models.Options.unique_together So your constraint should be more explicitly written as: `unique_together = (('journal_id', 'volume_number',),)`. – Tomasz Gandor Jan 18 '14 at 12:52
  • This set journal_id null=False – GrvTyagi Apr 01 '16 at 07:25
  • @araneae that's strange: you are right even if documentation says "The ValidationError raised during model validation when the constraint is violated has the unique_together error code." – Jarek Jakubowski Aug 11 '16 at 18:58
  • Is it possible to check unique together from 2 models? eg: I have a field a model A and b in Model B, and want to check the combintion of a and b is unique. – Jisson Aug 31 '18 at 05:49
  • 1
    Wow.. Django continues to amaze me with how easy it makes coding business logic! – robnardo Jan 03 '19 at 15:50
  • 1
    Use UniqueConstraint with the constraints option instead. UniqueConstraint provides more functionality than unique_together. unique_together may be deprecated in the future. – Akhil Mathew Aug 24 '22 at 12:20
228

Django 2.2+

Using the constraints features UniqueConstraint is preferred over unique_together.

From the Django documentation for unique_together:

Use UniqueConstraint with the constraints option instead.
UniqueConstraint provides more functionality than unique_together.
unique_together may be deprecated in the future.

For example:

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name="Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['journal_id', 'volume_number'], name='name of constraint')
        ]
Alonme
  • 1,364
  • 15
  • 28
daaawx
  • 3,273
  • 2
  • 17
  • 16
  • 1
    In what situation would the 'name' parameter of the UniqueConstraint be used for? I assume it works like the name parameter of a URL path? – user3750325 May 11 '19 at 17:06
  • 2
    @user7733611 naming the constraint can be helpful in a number of situations. For example if you are connecting to a legacy database, or if you just want the constraint names to be more human readable in the database. One time I migrated the character set of a MySQL database and Django's generated constraint names were actually too long for our particular target. – mihow Jul 12 '19 at 15:30
  • Not 100% sure it comes from `UniqueConstraint` but I get weird `psycopg2.errors.DuplicateTable: relation "name_of_the_constraint" already exists` when I switch to Postgres – zar3bski Oct 17 '19 at 20:57
  • It should be noted that text fields like `CharField` can be case sensitive or case insensitive depending on your database configurations! – Advena Feb 22 '21 at 10:29
12

in Django 4.0,

The new *expressions positional argument of UniqueConstraint() enables creating functional unique constraints on expressions and database functions. For example:

from django.db import models
from django.db.models import UniqueConstraint
from django.db.models.functions import Lower


class MyModel(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    class Meta:
        constraints = [
            UniqueConstraint(
                Lower('first_name'),
                Lower('last_name').desc(),
                name='first_last_name_unique',
            ),
        ]
SuperNova
  • 25,512
  • 7
  • 93
  • 64
  • 1
    As of Django 4.1, you can now specify a `validation_error_message` argument in your constraint function. – arcanemachine Sep 25 '22 at 19:51
  • 2
    It's actually `violation_error_message` in case anybody is looking ... https://docs.djangoproject.com/en/4.1/ref/models/constraints/#violation-error-message – michjnich Dec 24 '22 at 08:53
7

Yes you can define more than one field as unique using Django class Meta as this example:

class Volume(models.Model):
    id = models.AutoField(primary_key=True)
    journal_id = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volume_number = models.CharField('Volume Number', max_length=100)
    comments = models.TextField('Comments', max_length=4000, blank=True)

    class Meta:
        unique_together = ('volume_number', 'journal_id') 

Note:
To make things go writes you should not add attribute unique=True to any field that you define in unique_together attribute otherwise it will not work as unique together.

Arash Hatami
  • 5,297
  • 5
  • 39
  • 59
Dhia Shalabi
  • 1,332
  • 1
  • 13
  • 29
3

In Django 4.1.1

from django.db import models

class Volume(models.Model):
    field_1 = models.CharField("field 1", max_length=100,)
    field_2 = models.CharField("field 2", max_length=100,)
    field_3 = models.CharField("field 3", max_length=100,)


class Meta:
    unique_together = [['field_1', 'field_2']]
Yacine Bs
  • 182
  • 1
  • 13
  • For convenience, unique_together can be a single list when dealing with a single set of fields: ```unique_together = ['field_1', 'field_2']``` – dtar Feb 19 '23 at 14:58