Django: Mengapa beberapa bidang model saling berbenturan?

174

Saya ingin membuat objek yang berisi 2 tautan ke Pengguna. Sebagai contoh:

class GameClaim(models.Model):
    target = models.ForeignKey(User)
    claimer = models.ForeignKey(User)
    isAccepted = models.BooleanField()

tapi saya mendapatkan kesalahan berikut saat menjalankan server:

  • Accessor untuk bentrokan 'target' bidang dengan bidang terkait 'User.gameclaim_set'. Tambahkan argumen terkait_name ke definisi untuk 'target'.

  • Accessor untuk bentrok 'penggugat' bidang dengan bidang terkait 'User.gameclaim_set'. Tambahkan argumen related_name ke definisi untuk 'penuntut'.

Bisakah Anda jelaskan mengapa saya mendapatkan kesalahan dan bagaimana cara memperbaikinya?

Oleg Tarasenko
sumber
Pesan-pesan kesalahan ini sangat bagus. Mereka sudah menjelaskan cara memperbaikinya. Dan membaca di ** [ related_namedalam dokumentasi] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) akan menjelaskan mengapa itu terjadi.
Lutz Prechelt

Jawaban:

294

Anda memiliki dua kunci asing untuk Pengguna. Django secara otomatis membuat relasi terbalik dari Pengguna kembali ke GameClaim, yang biasanya gameclaim_set. Namun, karena Anda memiliki dua FK, Anda akan memiliki dua gameclaim_setatribut, yang jelas tidak mungkin. Jadi, Anda perlu memberi tahu Django nama apa yang digunakan untuk relasi terbalik.

Gunakan related_nameatribut dalam definisi FK. misalnya

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()
Daniel Roseman
sumber
49
Jawaban yang bagus, tetapi saya pikir Anda tidak berhasil menghindari kekasaran: P "Kenapa" tidak jelas kecuali Anda sadar bagaimana django bekerja secara internal.
Kenny
14
Untuk seseorang yang baru belajar kerangka kerja, ini tidak akan terlihat jelas.
jkyle
3
Terima kasih, pesan kesalahannya juga tidak jelas bagi saya, tetapi penjelasan Anda tentang hubungan sebaliknya sangat membantu.
ruquay
1
Hanya karena Clash adalah band yang baik, tidak membuat mereka menjadi pesan kesalahan yang deskriptif;)
btown
7
Juga harus disebutkan bahwa jika Anda tidak perlu menggunakan hubungan terbalik untuk semua model. Dalam beberapa kasus Anda mungkin menginginkan hubungan model menjadi satu arah. Dalam hal ini Anda menggunakan related_name = '+'. Ini memberitahu Django untuk membuat hubungan satu arah dan mengabaikan hubungan terbalik.
Tommy Strand
8

The Usermodel mencoba untuk membuat dua bidang dengan nama yang sama, satu untuk GameClaimsyang memiliki yang Usersebagai target, dan satu lagi untuk GameClaimsyang memiliki yang Usersebagai claimer. Inilah dokumen yangrelated_name , yang merupakan cara Django untuk membiarkan Anda mengatur nama atribut sehingga yang di-autogenerasi tidak bertentangan.

Hank Gay
sumber
7

OP tidak menggunakan kelas dasar abstrak ... tetapi jika Anda melakukannya, Anda akan menemukan bahwa pengkodean sulit terkait_name di FK (misalnya ..., related_name = "myname") akan menghasilkan sejumlah kesalahan konflik ini - satu untuk setiap kelas warisan dari kelas dasar. Tautan yang disediakan di bawah ini berisi solusi, yang sederhana, tetapi jelas tidak jelas.

Dari django docs ...

Jika Anda menggunakan atribut related_name pada ForeignKey atau ManyToManyField, Anda harus selalu menentukan nama terbalik unik untuk bidang tersebut. Ini biasanya akan menyebabkan masalah dalam kelas dasar abstrak, karena bidang pada kelas ini dimasukkan ke dalam masing-masing kelas anak, dengan nilai yang persis sama untuk atribut (termasuk related_name) setiap kali.

Info lebih lanjut di sini .

Pascal Polleunus
sumber
2

Terkadang Anda harus menggunakan pemformatan tambahan di related_name - sebenarnya, kapan saja ketika pewarisan digunakan.

class Value(models.Model):
    value = models.DecimalField(decimal_places=2, max_digits=5)
    animal = models.ForeignKey(
        Animal, related_name="%(app_label)s_%(class)s_related")

    class Meta:
        abstract = True

class Height(Value):
    pass

class Weigth(Value):
    pass

class Length(Value):
    pass

Tidak ada bentrokan di sini, tetapi related_name didefinisikan sekali dan Django akan berhati-hati untuk membuat nama hubungan yang unik.

kemudian pada anak-anak kelas Value, Anda akan memiliki akses ke:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Sławomir Lenart
sumber
0

Saya sepertinya menemukan ini kadang-kadang ketika saya menambahkan submodule sebagai aplikasi untuk proyek Django, misalnya diberikan struktur berikut:

myapp/
myapp/module/
myapp/module/models.py

Jika saya menambahkan yang berikut ke INSTALLED_APPS:

'myapp',
'myapp.module',

Django tampaknya memproses file myapp.mymodule models.py dua kali dan melempar kesalahan di atas. Ini dapat diatasi dengan tidak menyertakan modul utama dalam daftar INSTALLED_APPS:

'myapp.module',

Termasuk myappbukanmyapp.module menyebabkan semua tabel database dibuat dengan nama yang salah, jadi ini sepertinya cara yang benar untuk melakukannya.

Saya menemukan posting ini sambil mencari solusi untuk masalah ini jadi saya pikir saya akan menempatkan ini di sini :)

Jordan Hagan
sumber
0

Hanya menambahkan jawaban Jordan (terima kasih untuk tip Jordan) itu juga dapat terjadi jika Anda mengimpor level di atas aplikasi dan kemudian mengimpor aplikasi mis.

myproject/ apps/ foo_app/ bar_app/

Jadi jika Anda mengimpor aplikasi, foo_app dan bar_app maka Anda bisa mendapatkan masalah ini. Saya punya aplikasi, foo_app dan bar_app semua tercantum dalam pengaturan.INSTALLED_APPS

Dan Anda ingin menghindari mengimpor aplikasi, karena Anda memiliki aplikasi yang sama terpasang di 2 ruang nama yang berbeda

apps.foo_app dan foo_app

lukeaus
sumber