Cách thể hiện mối quan hệ Một-Nhiều trong Django


167

Tôi đang xác định các mô hình Django của mình ngay bây giờ và tôi nhận ra rằng không có OneToManyFieldloại mô hình nào trong trường. Tôi chắc chắn có một cách để làm điều này, vì vậy tôi không chắc mình đang thiếu gì. Tôi về cơ bản có một cái gì đó như thế này:

class Dude(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

class PhoneNumber(models.Model):
    number = models.CharField()

Trong trường hợp này, mỗi Dudecó thể có nhiều PhoneNumbers, nhưng mối quan hệ nên có một chiều, trong đó tôi không cần phải biết từ PhoneNumberđó Dudesở hữu nó, cho mỗi gia nhập, như tôi có thể có nhiều đối tượng khác nhau mà riêng PhoneNumbertrường hợp, chẳng hạn như một Businesscho thí dụ:

class Business(models.Model):
    numbers = models.OneToManyField('PhoneNumber')

Tôi sẽ thay thế cái gì OneToManyField(không tồn tại) trong mô hình để thể hiện mối quan hệ kiểu này? Tôi đến từ Hibernate / JPA nơi tuyên bố mối quan hệ một-nhiều dễ dàng như:

@OneToMany
private List<PhoneNumber> phoneNumbers;

Làm thế nào tôi có thể thể hiện điều này trong Django?

Câu trả lời:


133

Để xử lý các mối quan hệ Một-Nhiều trong Django, bạn cần sử dụng ForeignKey.

Tài liệu về ForeignKey rất toàn diện và sẽ trả lời tất cả các câu hỏi bạn có:

https://docs.djangoproject.com/en/dev/ref/models/fields/#forignkey

Cấu trúc hiện tại trong ví dụ của bạn cho phép mỗi Dude có một số và mỗi số thuộc về nhiều Dudes (giống với Business).

Nếu bạn muốn mối quan hệ đảo ngược, bạn sẽ cần thêm hai trường ForeignKey vào mô hình PhoneNumber của bạn, một cho Dude và một cho Doanh nghiệp. Điều này sẽ cho phép mỗi số thuộc về một Dude hoặc một Doanh nghiệp và có Dudes và Doanh nghiệp có thể sở hữu nhiều Số. Tôi nghĩ rằng đây có thể là những gì bạn đang theo đuổi.

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

3
Bạn có thể cho tôi một ví dụ cho vấn đề ở trên? Có lẽ tôi chỉ thiếu nó hoàn toàn, nhưng tôi đã đọc tài liệu Django một thời gian rồi và vẫn chưa rõ làm thế nào để tạo ra mối quan hệ kiểu này.
Naftuli Kay

4
có thể muốn làm cho cả hai khóa ngoại không bắt buộc (blank = True, null = True) hoặc thêm một số loại xác thực tùy chỉnh để đảm bảo rằng có ít nhất cái này hoặc cái kia. Còn trường hợp một doanh nghiệp có số chung thì sao? hoặc một anh chàng thất nghiệp?
j_syk

1
@j_syk điểm tốt về xác nhận tùy chỉnh. nhưng có vẻ như rất khó để bao gồm cả một khóa ngoại cho anh chàng và một người nước ngoài để kinh doanh, và sau đó thực hiện xác nhận tùy chỉnh (bên ngoài định nghĩa mô hình). có vẻ như phải có một cách sạch hơn, nhưng tôi cũng không thể hiểu được.
andy

Trong tình huống này, phù hợp để sử dụng Khung ContentTypes cho phép các mối quan hệ chung với các đối tượng khác nhau. Tuy nhiên, việc sử dụng các loại nội dung có thể trở nên rất phức tạp rất nhanh và nếu đây là nhu cầu duy nhất của bạn, phương pháp đơn giản (nếu hacky) này có thể được mong muốn.
kball

57
Một điều liên quan cần đề cập là đối số "Rel_name" với ForeignKey. Vì vậy, trong lớp PhoneNumber bạn có dude = models.ForeignKey(Dude, related_name='numbers')và sau đó bạn có thể sử dụng some_dude_object.numbers.all()để nhận được tất cả các số liên quan (nếu bạn không chỉ định một "tên liên quan", nó sẽ mặc định là "number_set").
markshep

40

Trong Django, mối quan hệ một-nhiều được gọi là ForeignKey. Tuy nhiên, nó chỉ hoạt động theo một hướng, vì vậy thay vì có một numberthuộc tính của lớp Dudebạn sẽ cần

class Dude(models.Model):
    ...

class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)

Nhiều mô hình có thể có một ForeignKeymô hình khác, do đó, sẽ có giá trị nếu có thuộc tính thứ hai PhoneNumbernhư vậy

class Business(models.Model):
    ...
class Dude(models.Model):
    ...
class PhoneNumber(models.Model):
    dude = models.ForeignKey(Dude)
    business = models.ForeignKey(Business)

Bạn có thể truy cập vào PhoneNumbers cho một Dudeđối tượng dvới d.phonenumber_set.objects.all(), và sau đó làm tương tự cho một Businessđối tượng.


1
Tôi đã theo giả định ForeignKeycó nghĩa là "một-một". Sử dụng ví dụ trên của bạn, tôi nên có một Dudecái có nhiều PhoneNumbersphải không?
Naftuli Kay

6
Tôi chỉnh sửa câu trả lời của tôi để phản ánh điều này. Đúng. ForeignKeychỉ là một đối một nếu bạn chỉ định ForeignKey(Dude, unique=True), vì vậy với đoạn mã trên, bạn sẽ nhận được một Dudevới nhiều PhoneNumbers.
stillLearning

1
@rolling đá- cảm ơn bạn, tôi đã thêm rằng sau khi nhận ra sai lầm của tôi như bạn nhận xét. Unique = True không hoạt động chính xác như OneToOneField, tôi muốn giải thích rằng ForeignKey chỉ sử dụng mối quan hệ một-một nếu bạn chỉ định Unique = True.
stillLearning

8
+1 để làm điều đó từ PhoneNumber. Bây giờ nó bắt đầu có ý nghĩa. ForeignKeyvề cơ bản là nhiều-một-một, vì vậy bạn cần thực hiện ngược lại để có được một-nhiều-nhiều :)
Naftuli Kay

2
Ai đó có thể giải thích tầm quan trọng của tên của lĩnh vực này phonenumber_set? Tôi không thấy nó được định nghĩa ở bất cứ đâu. Có phải đó là tên của mô hình, trong tất cả các chữ thường, được gắn thêm "_set"?
James Wierzba


15

Bạn có thể sử dụng khóa ngoại ở nhiều phía của OneToManyquan hệ (nghĩa là ManyToOnequan hệ) hoặc sử dụng ManyToMany(ở bất kỳ phía nào) với ràng buộc duy nhất.


8

djangolà đủ thông minh. Thật ra chúng ta không cần xác định oneToManytrường. Nó sẽ được tự động tạo djangocho bạn :-). Chúng ta chỉ cần xác định foreignKeytrong bảng liên quan. Nói cách khác, chúng ta chỉ cần xác định ManyToOnemối quan hệ bằng cách sử dụng foreignKey.

class Car(models.Model):
    // wheels = models.oneToMany() to get wheels of this car [**it is not required to define**].


class Wheel(models.Model):
    car = models.ForeignKey(Car, on_delete=models.CASCADE)  

nếu chúng ta muốn có được danh sách các bánh xe của chiếc xe cụ thể. chúng tôi sẽ sử dụng python'sđối tượng được tạo tự động wheel_set. Đối với xe cbạn sẽ sử dụngc.wheel_set.all()


5

Mặc dù câu trả lời của đá lăn là tốt, đơn giản và đầy đủ chức năng, tôi nghĩ có hai điều nó không giải quyết được.

  1. Nếu OP muốn thực thi một số điện thoại thì không thể thuộc về cả anh chàng và doanh nghiệp
  2. Cảm giác buồn bã không thể giải thích là kết quả của việc xác định mối quan hệ trên mô hình PhoneNumber chứ không phải trên mô hình Dude / Business. Khi những người ngoài hành tinh đến Trái đất và chúng tôi muốn thêm một mô hình Alien, chúng tôi cần sửa đổi PhoneNumber (giả sử các ET có số điện thoại) thay vì chỉ thêm trường "phone_numbers" vào mô hình Alien.

Giới thiệu khung loại nội dung , hiển thị một số đối tượng cho phép chúng tôi tạo "khóa ngoại chung" trên mô hình PhoneNumber. Sau đó, chúng ta có thể xác định mối quan hệ ngược lại trên Dude và Business

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

class PhoneNumber(models.Model):
    number = models.CharField()

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    owner = GenericForeignKey()

class Dude(models.Model):
    numbers = GenericRelation(PhoneNumber)

class Business(models.Model):
    numbers = GenericRelation(PhoneNumber)

Xem tài liệu để biết chi tiết, và có lẽ kiểm tra bài viết này để được hướng dẫn nhanh.

Ngoài ra, đây là một bài viết lập luận chống lại việc sử dụng FK chung.


0

Nếu mô hình "nhiều" không biện minh cho việc tạo ra một mô hình per-se (không phải trường hợp ở đây, nhưng nó có thể mang lại lợi ích cho người khác), thì một cách khác sẽ là dựa vào các loại dữ liệu PostgreQuery cụ thể, thông qua gói Django Contrib

Postgres thể đối phó với mảng hoặc JSON kiểu dữ liệu, và điều này có thể là một cách giải quyết tốt đẹp để xử lý One-To-Nhiều khi nhiều-tệ chỉ có thể được gắn với một thực thể duy nhất của một .

Postgres cho phép bạn truy cập các phần tử đơn lẻ của mảng, điều đó có nghĩa là các truy vấn có thể thực sự nhanh và tránh các chi phí cấp độ ứng dụng. Và tất nhiên, Django triển khai một API tuyệt vời để tận dụng tính năng này.

Nó rõ ràng có nhược điểm là không thể di chuyển đến phần cuối cơ sở dữ liệu của người khác, nhưng tôi cho rằng nó vẫn đáng được đề cập.

Hy vọng nó có thể giúp một số người tìm kiếm ý tưởng.


0

Trước hết chúng tôi đi tham quan:

01) mối quan hệ một-nhiều:

ASSUME:

class Business(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class Dude(models.Model):
    name = models.CharField(max_length=200)
    .........
    .........
    phone_number = models.OneToMany(PhoneNumber) (NB: Django do not support OneToMany relationship)

class PhoneNumber(models.Model):
    number = models.CharField(max_length=20)
    ........
    ........

Lưu ý: Django không cung cấp bất kỳ mối quan hệ OneToMany nào. Vì vậy, chúng tôi không thể sử dụng phương pháp trên trong Django. Nhưng chúng ta cần chuyển đổi trong mô hình quan hệ. Vậy chúng ta có thể làm gì? Trong tình huống này, chúng ta cần chuyển đổi mô hình quan hệ thành mô hình quan hệ ngược.

Đây:

mô hình quan hệ = OneToMany

Vì vậy, mô hình quan hệ ngược = ManyToOne

Lưu ý: Django hỗ trợ mối quan hệ ManyToOne & trong Django ManyToOne được đại diện bởi ForeignKey.

02) mối quan hệ nhiều-một:

SOLVE:

class Business(models.Model):
    .........
    .........

class Dude(models.Model):
    .........
    .........

class PhoneNumber(models.Model):
    ........
    ........
    business = models.ForeignKey(Business)
    dude = models.ForeignKey(Dude)

NB: NGHINK ĐƠN GIẢN !!

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.