Làm thế nào để tạo một đối tượng cho mô hình Django với nhiều lĩnh vực?


138

Mô hình của tôi:

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

Tôi muốn lưu cả hai user1user2trong mô hình đó:

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

Tôi biết điều đó là sai, nhưng tôi chắc chắn bạn sẽ có được những gì tôi muốn làm. Bạn sẽ làm điều này như thế nào ?

Câu trả lời:


241

Bạn không thể tạo quan hệ m2m từ các đối tượng chưa được lưu. Nếu bạn có pks, hãy thử điều này:

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

Cập nhật: Sau khi đọc câu trả lời của saverio , tôi quyết định tìm hiểu sâu hơn một chút về vấn đề này. Dưới đây là những phát hiện của tôi.

Đây là đề nghị ban đầu của tôi. Nó hoạt động, nhưng không tối ưu. (Lưu ý: Tôi đang sử dụng Bars và a Foothay vì Users và a Sample, nhưng bạn hiểu ý tưởng).

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

Nó tạo ra tổng cộng 7 truy vấn:

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)

Tôi chắc rằng chúng ta có thể làm tốt hơn. Bạn có thể truyền nhiều đối tượng cho add()phương thức:

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

Như chúng ta có thể thấy, truyền nhiều đối tượng sẽ lưu một 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)

Tôi không biết rằng bạn cũng có thể chỉ định một danh sách các đối tượng:

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

Thật không may, điều đó tạo ra một bổ sung 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)

Hãy thử gán một danh sách pks, như saverio đề xuất:

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

Khi chúng tôi không tìm nạp hai Bars, chúng tôi lưu hai SELECTcâu lệnh, dẫn đến tổng cộng 5:

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)

Và người chiến thắng là:

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

Truyền pks để add()cung cấp cho chúng tôi tổng cộng 4 truy vấn:

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
Tôi muốn thêm, bạn có thể vượt qua một danh sách với * like so: foo.bars.add (* list) và nó sẽ làm nổ danh sách thành các đối số: D
Guillermo Siliceo Trueba 22/12/13

1
Đây phải là Tài liệu Django về ManyToMany! rõ ràng hơn nhiều rồi docs.djangoproject.com/en/1.10/topics/db/examples/many_to_many hoặc docs.djangoproject.com/en/1.10/ref/models/fields và cũng có các hình phạt hiệu suất cho phương pháp khác nhau được bao gồm. Có lẽ bạn có thể cập nhật nó cho Django 1.9? (phương pháp đã đặt)
gabn88 20/03/2017

Tôi muốn lưu mô hình với một id với nhiều hơn một mục và số lượng. Nó sẽ có thể chứ ?? class Cart (model.Model): item = model.ForeignKey (Item, verbose_name = "item") quant = model.PositiveIntegerField (default = 1)
Nitesh

Ồ Tôi ngạc nhiên. : D
М.erl.

111

Đối với du khách trong tương lai, bạn có thể tạo một đối tượng và tất cả các đối tượng m2m của nó trong 2 truy vấn bằng cách sử dụng mới bulk_create trong django 1.4. Lưu ý rằng điều này chỉ có thể sử dụng được nếu bạn không yêu cầu bất kỳ quá trình xử lý trước hoặc hậu xử lý nào trên dữ liệu bằng các phương thức hoặc tín hiệu save (). Những gì bạn chèn là chính xác những gì sẽ có trong DB

Bạn có thể làm điều này mà không chỉ định mô hình "thông qua" trên trường. Để hoàn thiện, ví dụ dưới đây tạo ra một mô hình Người dùng trống để bắt chước những gì người đăng ban đầu đã hỏi.

from django.db import models

class Users(models.Model):
    pass

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

Bây giờ, trong một shell hoặc mã khác, tạo 2 người dùng, tạo một đối tượng mẫu và thêm số lượng lớn người dùng vào đối tượng mẫu đó.

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)
])

Tôi đang cố gắng sử dụng câu trả lời của bạn ở đây nhưng tôi đang bị mắc kẹt. Suy nghĩ?
Pureferret 10/2/2015

Tuy nhiên, chắc chắn sẽ có nhiều thông tin để biết người ta có thể làm điều này mà không cần một lớp Trung cấp (thông qua) được xác định rõ ràng, tuy nhiên, để dễ đọc mã bằng cách sử dụng lớp Trung cấp. Xem ở đây .
colm.anseo

giải pháp tuyệt vời!
pymarco

19

Django 1.9
Một ví dụ nhanh:

sample_object = Sample()
sample_object.save()

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

8

Các đối tượng liên quan là "thuộc tính" khác với các trường trong Mô hình. Cách đơn giản nhất để đạt được những gì bạn đang tìm kiếm là

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

Điều đó giống như việc gán danh sách Người dùng, không có các truy vấn bổ sung và xây dựng mô hình.

Nếu số lượng truy vấn là điều làm phiền bạn (thay vì đơn giản), thì giải pháp tối ưu yêu cầu ba truy vấn:

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)

Điều này sẽ hoạt động vì chúng tôi đã biết rằng danh sách 'người dùng' trống, vì vậy chúng tôi có thể tạo ra một cách không suy nghĩ.


2

Bạn có thể thay thế tập hợp các đối tượng liên quan theo cách này (mới trong Django 1.9):

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

0

Nếu ai đó đang tìm cách trả lời David Marble trên trường ManyToMany tự giới thiệu. Các id của mô hình thông qua được gọi là: "to_'model_name_id" và "from_'model_name'_id".

Nếu điều đó không làm việc, bạn có thể kiểm tra kết nối django.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.