Çoktan çok alana sahip bir Django modeli için bir nesne nasıl oluşturulur?


138

Benim modelim:

class Sample(models.Model):
    users = models.ManyToManyField(User)

Her ikisini de user1ve user2bu modelde kaydetmek istiyorum :

user1 = User.objects.get(pk=1)
user2 = User.objects.get(pk=2)
sample_object = Sample(users=user1, users=user2)
sample_object.save()

Bunun yanlış olduğunu biliyorum, ama eminim yapmak istediğim şeyi elde edersiniz. Nasıl yapardın ?

Yanıtlar:


241

Kaydedilmemiş nesnelerden m2m ilişkileri oluşturamazsınız. Eğer pks varsa, şunu deneyin:

sample_object = Sample()
sample_object.save()
sample_object.users.add(1,2)

Güncelleme: Koruyucu cevabını okuduktan sonra , sorunu biraz daha araştırmaya karar verdim. İşte bulgularım.

Bu benim orijinal önerimdi. Çalışır, ancak optimal değildir. (Not: Bars ve a Fooyerine Users ve yerine kullanıyorum Sample, ama fikri anlıyorsunuz).

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1)
foo.bars.add(bar2)

Toplam 7 sorgu oluşturur:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

Eminim daha iyisini yapabiliriz. add()Yönteme birden fazla nesne iletebilirsiniz :

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars.add(bar1, bar2)

Gördüğümüz gibi, birden fazla nesne iletmek bir tane kurtarır SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

Nesnelerin bir listesini de atayabileceğinizin farkında değildim:

bar1 = Bar.objects.get(pk=1)
bar2 = Bar.objects.get(pk=2)
foo = Foo()
foo.save()
foo.bars = [bar1, bar2]

Ne yazık ki, bu bir ek oluşturur SELECT:

SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 1
SELECT "app_bar"."id", "app_bar"."name" FROM "app_bar" WHERE "app_bar"."id" = 2
INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

pkSaverio'nun önerdiği gibi bir s listesi atamaya çalışalım:

foo = Foo()
foo.save()
foo.bars = [1,2]

İki kelimeyi getirmediğimiz için Bariki SELECTifadeyi kaydederek toplamda 5 elde ederiz :

INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."id", "app_foo_bars"."foo_id", "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE "app_foo_bars"."foo_id" = 1
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

Ve kazanan:

foo = Foo()
foo.save()
foo.bars.add(1,2)

Geçme pkiçin s add()bize 4 sorgularının toplam verir:

INSERT INTO "app_foo" ("name") VALUES ()
SELECT "app_foo_bars"."bar_id" FROM "app_foo_bars" WHERE ("app_foo_bars"."foo_id" = 1  AND "app_foo_bars"."bar_id" IN (1, 2))
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 1)
INSERT INTO "app_foo_bars" ("foo_id", "bar_id") VALUES (1, 2)

25
Eklemek istiyorum, * gibi bir liste geçirebilirsiniz: foo.bars.add (* list) ve listeyi argümanlara patlatacaktır: D
Guillermo Siliceo Trueba

1
Bu ManyToMany hakkındaki Django Docs olmalı! docs.djangoproject.com/en/1.10/topics/db/examples/many_to_many veya docs.djangoproject.com/en/1.10/ref/models/fields ve ayrıca dahil edilen farklı yöntem için performans cezalarıyla çok daha net . Belki de Django 1.9 için güncelleyebilirsiniz? (set yöntemi)
gabn88

Modeli birden fazla öğe ve miktar ile tek kimliği ile kaydetmek istiyorum. Mümkün olacak mı?? sınıf sepeti (models.Model): öğe = models.ForeignKey (Öğe, verbose_name = "item") miktarı = models.PositiveIntegerField (default = 1)
Nitesh

Vay. Şaşırdım. : D
М.Б.

111

Gelecekteki ziyaretçiler için, django 1.4'teki yeni bulk_create'yi kullanarak 2 sorguda bir nesne ve tüm m2m nesnelerini oluşturabilirsiniz . Bunun yalnızca save () yöntemlerine veya sinyallerine sahip veriler üzerinde herhangi bir ön işleme veya son işleme gerek duymuyorsanız kullanılabilir olduğunu unutmayın . Eklediğiniz şey DB'de tam olarak ne olacak

Bunu, alanda "içinden" modeli belirtmeden yapabilirsiniz. Tamlık için aşağıdaki örnek, orijinal posterin ne istediğini taklit etmek için boş bir Kullanıcılar modeli oluşturur.

from django.db import models

class Users(models.Model):
    pass

class Sample(models.Model):
    users = models.ManyToManyField(Users)

Artık bir kabukta veya başka bir kodda 2 kullanıcı oluşturun, örnek bir nesne oluşturun ve kullanıcıları bu örnek nesneye toplu olarak ekleyin.

Users().save()
Users().save()

# Access the through model directly
ThroughModel = Sample.users.through

users = Users.objects.filter(pk__in=[1,2])

sample_object = Sample()
sample_object.save()

ThroughModel.objects.bulk_create([
    ThroughModel(users_id=users[0].pk, sample_id=sample_object.pk),
    ThroughModel(users_id=users[1].pk, sample_id=sample_object.pk)
])

Cevabınızı burada kullanmaya çalışıyorum ama takıldım. Düşünceler?
Pureferret

açıkça tanımlanmış bir Orta (sınıf) sınıf olmadan bunu yapabileceğinizi bilmek kesinlikle bilgilendiricidir, ancak bir Orta sınıf kullanarak kod okunabilirliği için önerilir. Buraya bakın .
colm.anseo

harika bir çözüm!
pymarco

19

Django 1.9
Kısa bir örnek:

sample_object = Sample()
sample_object.save()

list_of_users = DestinationRate.objects.all()
sample_object.users.set(list_of_users)

8

RelatedObjectManagers bir Modeldeki alanlardan farklı "nitelikler" dir. Aradığınızı elde etmenin en basit yolu

sample_object = Sample.objects.create()
sample_object.users = [1, 2]

Bu, ek sorgular ve model oluşturma olmadan bir Kullanıcı listesi atamakla aynıdır.

Sorgu sayısı sizi rahatsız eden şeyse (basitlik yerine), en uygun çözüm üç sorgu gerektirir:

sample_object = Sample.objects.create()
sample_id = sample_object.id
sample_object.users.through.objects.create(user_id=1, sample_id=sample_id)
sample_object.users.through.objects.create(user_id=2, sample_id=sample_id)

Bu işe yarayacaktır, çünkü 'kullanıcılar' listesinin boş olduğunu zaten biliyoruz, böylece dikkatsizce oluşturabiliriz.


2

İlgili nesne kümesini bu şekilde değiştirebilirsiniz (Django 1.9'da yeni olan):

new_list = [user1, user2, user3]
sample_object.related_set.set(new_list)

0

Birisi yapmak istiyorsa David Marbles, ManyToMany alanına atıfta bulunarak kendi kendine cevap verir. Through modelinin kimlikleri "to_'model_name_id" ve "from_'model_name'_id" olarak adlandırılır.

Bu işe yaramazsa django bağlantısını kontrol edebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.