2016-11-27 5 views
1

にnullのとき、私はこのモデルがあります:重複レコードtreeforeignkeyは、DjangoのMPTT

class Genre(MPTTModel): 
    id = models.CharField(max_length=100) 
    name = models.CharField(max_length=100) 
    parent = TreeForeignKey(
     'self', 
     null=True, 
     blank=True, 
     related_name='subgenre' 
    ) 

    def __str__(self): 
     return self.name 

    class Meta: 
     unique_together = (('id', 'parent'),) 

私は重複するレコードを持っている必要はありませんでしたので、私はidとTreeForeignKeyでunique_togetherを使用しています。

unique_togetherであっても、親をnullに設定すると、重複を追加できます。どうすればそれを避けることができますか?

答えて

0

これはSQL設計上の決定です。

SQL 2011 draft、ページ474読み取り:

T内の2つの行がない場合は1行の各列の値がnullで、対応する列の値から 明確ではないよう他の行では、結果は です。さもなければ、の結果はFalseです。

これは、ユニーク制約になると、2つのNULL値が異なるとみなされることを意味します。これは、41ページのNULLデータ型定義と矛盾します。

2つのNULL値は区別されません。

ヌル値とヌル以外の値は区別されます。

サブクエリ8.15、 ""の一般規則がTrueを返す場合、2つのnull以外の値は区別されます。細分箇条8.15の

一般規則は言う:

V1とV2の両方がNULL値である場合、結果はFalseです。

を要約すると:

それはデータ型になると、2つのNULLの「特殊性は、」偽意味NULL == NULLです。

ただし、テーブルレベルの一意性制約には、そうでない場合はNULLが指定されています。テーブルのフィールドには一意でなければならないと言われる多くのNULLがあります。

Djangoチケットの追跡は#1751 unique_together does not work when any of the listed fields contains a FKです。回避策は、documentationで説明されているように、独自の.validate_uniqueモデルメソッドを定義することです。

from django.core.exceptions import ValidationError 
from django.db import transaction 

def validate_unique(self, exclude=None): 
    with transaction.atomic(): 
     if Genre.objects.select_for_update().filter(parent=self.parent, id=self.id).exists(): 
      params = { 
       'model_name': _('Genre'), 
       'field_labels': _('Parent and ID') 
      } 
      raise ValidationError(
       message=_(
        '%(model_name)s with this %(field_labels)s already exists.' 
       ), code='unique_together', params=params, 
      ) 

select_for_updateは競合状態を避けるためにロックを作成します。

このソリューションはフォーム送信には使用できますが、Genre.objects.create()メソッドに直接アクセスすると機能しません。その場合は、Genreインスタンスを3つのステップで作成する必要があります。

genre = Genre(id='id1') 
genre.validate_unique() 
genre.save() 
関連する問題