12

既存のユーザー モデルの拡張 で説明されているように、「プロファイル」モデル (ユーザー モデルと 1 対 1 の関係を持つ) を作成しました。プロファイル モデルには、別のモデルとのオプションの多対 1 の関係があります。

class Profile(models.Model):
    user = models.OneToOneField(User, primary_key=True)
    account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)

そこに記載されているように、インライン管理者も作成しました。

class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'profiles'
# UserAdmin and unregister()/register() calls omitted, they are straight copies from the Django docs

ユーザーを作成するときに管理者でを選択しないaccountと、プロファイル モデルが作成されません。したがって、ドキュメントに従って、 post_save信号に接続します。

@receiver(post_save, sender=User)
def create_profile_for_new_user(sender, created, instance, **kwargs):
    if created:
        profile = Profile(user=instance)
        profile.save()

これは、管理者で を選択しない限り問題なく動作しますが、選択すると例外が発生し、次のように通知されます。accountIntegrityErrorduplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.

どうやら、インライン管理者はprofileインスタンス自体を作成しようとしますが、post_saveその時点でシグナル ハンドラーが既にインスタンスを作成しています。

次の要件をすべて維持しながら、この問題を解決するにはどうすればよいですか?

  1. 新しいユーザーがどのように作成さprofileれても、後でそれにリンクするモデルが常に存在します。
  2. accountユーザーの作成時にユーザーが admin でを選択すると、後でaccount新しいprofileモデルに設定されます。そうでない場合、フィールドはnull.

環境: Django 1.5、Python 2.7

関連する質問:

4

2 に答える 2

24

この問題は、モデルを指すように設定primary_key=Trueすることで回避できます。OneToOneFieldUser

これが機能する理由はかなり単純なようです。

モデル インスタンスを作成して保存する前に手動で設定しようとすると、Django はやみくもに新しいレコードを作成しようとするのではなくpk、データベース内のレコードを見つけて更新しようとします。pk存在しない場合は、期待どおりに新しいレコードが作成されます。

を主キーとして設定しOneToOneField、Django 管理者がそのフィールドを関連するUserモデルの ID に設定すると、pkはすでに設定されており、Django は最初に既存のレコードを見つけようとします。

OneToOneFieldセットを主キーとして使用すると、次のようになります。

  1. Django 管理者は新しいUserインスタンスを作成しますid
  2. Django 管理者がUserインスタンスを保存します。
    1. pk(この場合は ) が設定されていないためid、Django は新しいレコードを作成しようとします。
    2. 新しいレコードidは、データベースによって自動的に設定されます。
    3. post_saveフックは、そのインスタンスの新しいインスタンスを作成しProfileますUser
  3. Django Admin は新しいProfileインスタンスを作成し、そのuserセットをユーザーの に設定しますid
  4. Django 管理者がProfileインスタンスを保存します。
    1. pk(この場合は ) が既に設定されているためuser、Django はその を使用して既存のレコードを取得しようとしpkます。
    2. Django は既存のレコードを見つけて更新します。

主キーを明示的に設定しない場合、Django は代わりにデータベースのauto_increment機能を使用するフィールドを追加します。データベースは、pk存在しない次に大きな値を設定します。これは、手動で設定しない限りフィールドが実際には空白のままであることを意味し、Django は常に新しいレコードを挿入しようとするため、 の一意性制約と競合しOneToOneFieldます。

これが元の問題の原因です。

  1. Django 管理者は新しいUserインスタンスを作成しますid
  2. Django Admin はUserインスタンスを保存し、以前と同様にpost_saveフックが新しいProfileインスタンスを作成します。
  3. Django 管理者は、 no (自動的に追加されたフィールド)なしで新しいProfileインスタンスを作成します。idpk
  4. Django 管理者がProfileインスタンスを保存します。
    1. pk(この場合は ) が設定されていないためid、Django は新しいレコードを作成しようとします。
    2. データベースは、フィールドに対するテーブルの一意性制約の違反を報告しuserます。
    3. Django は例外をスローします。今日は宇宙に行きません。
于 2013-08-16T03:05:11.310 に答える
2

プロファイル モデルをモデルに接続するように設定primary_key=Trueすると、この問題が解決するようです。ただし、その意味とそれが役立つ理由をすべて理解しているとは思いません。OneToOneFieldUser

これをヒントとしてここに残しておきますが、それが最善の解決策であり、誰かが適切に書かれた説明を思い付くことができれば、私はそれを支持/受け入れ、おそらく私のものを削除します.

于 2013-01-15T19:23:55.063 に答える