简体   繁体   English

Django 多对多关系

[英]Django ManyToMany relationship

In models.py :models.py 中

from django.db import models
from django.utils.translation import ugettext as _

# Create your models here.
class Category(models.Model):
    name            = models.CharField(_(u"Name"), max_length=250)
    products        = models.ManyToManyField("Product", verbose_name=_(u"Products"), \
                      blank=True, null=True, related_name="+")
        
class Product(models.Model):
    name            = models.CharField(_(u"Name"), max_length=250)    
    category        = models.ManyToManyField("Category", verbose_name=_(u"Category"), \
                      blank=True, null=True, related_name="+")

In admin page:在管理页面:

m2m关系

The question:问题:
How can the relations between products and category m2m fields in models.py be set so that in admin-page, as it can be seen at the picture, b2 (the product) was marked as it belongs to a2 (the category).如何设置models.pyproductscategory m2m 字段之间的关系,以便在管理页面中,如图所示, b2 (产品)被标记为属于a2 (类别)。
Any advices for implementation of [products, categories] are welcomed, thank you.欢迎就 [产品、类别] 的实施提出任何建议,谢谢。

PS聚苯乙烯
I'm newbie in Django. Sorry for my English.我是 Django 的新手。对不起我的英语。

The problem is that you have two ManyToMany fields. 问题是你有两个 ManyToMany字段。 As you've noted, when the relationship is set in one of them, it isn't in the other. 正如您所指出的那样,当关系设置在其中一个中时,它就不在另一个中。

The solution is simple: drop one of the fields. 解决方案很简单:删除其中一个字段。 You only need a ManyToManyField on one side of the relationship. 您只需要在关系的一侧使用ManyToManyField。 Django gives you access to the other side automatically. Django让您自动访问另一方。 So, if you kept the categories field on the Product model, you can do my_product.categories.all() to get the categories a product is associated with; 因此,如果您在Product型号上保留了categories字段,则可以执行my_product.categories.all()以获取与产品相关联的类别; and my_category.product_set.all() to get the products that belong to a category. my_category.product_set.all()获取属于某个类别的产品。

You'll also need to drop the related_name="+" attribute: you've presumably put that in because you were getting conflicts, which should have been a clue. 你还需要删除related_name="+"属性:你可能会把它放进去因为你遇到了冲突,这应该是一个线索。

Mar, 2022 Update: 2022 年 3 月更新:

There are 2 ways to create "many-to-many" relationship in Django .Django中有两种创建“多对多”关系的方法。 One doesn't use "ManyToManyField()" and one uses "ManyToManyField()" .一个不使用“ManyToManyField()” ,一个使用“ManyToManyField()” First of all, I'll show you the way which doesn't use "ManyToManyField()" .首先,我将向您展示不使用"ManyToManyField()"的方法。

<The way without "ManyToManyField()"> <没有“ManyToManyField()”的方法>

To create many-to-many relationship , there must be the middle table between 2 other tables and it must have each foreign key from 2 other tables and these foreign keys must be unique together .要创建多对多关系,必须有2 个其他表之间的中间表,并且它必须具有来自 2 个其他表的每个外键,并且这些外键必须是唯一的 So, I created the middle table "CategoryProduct" between "Category" and "Product" tables and it has each foreign key from "Category" and "Product" tables and these foreign keys are unique together as shown below.因此,我在“Category”和“Product”表之间创建了中间表“CategoryProduct” ,它具有来自“Category”和“Product”表的每个外键这些外键是唯一的,如下所示。

"models.py" : “模型.py”

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=255)
    
    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=255)    
    
    def __str__(self):
        return self.name

# The middle table between "Category" and "Product" tables
class CategoryProduct(models.Model): 
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

    class Meta:
        unique_together = [['category', 'product']]

And this is how you inline the middle table "CategoryProduct" to "Product" table as shown below.这就是将中间表“CategoryProduct”内联到“Product”表的方式,如下所示。

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import CategoryProduct, Product

class CategoryProductInline(admin.TabularInline):
    model = CategoryProduct
    min_num = 1 
    extra = 2
    max_num = 5

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    inlines = [CategoryProductInline]

This is how it looks like:这是它的样子:

在此处输入图像描述

And this is how you inline the middle table "CategoryProduct" to "Category" table as shown below.这就是将中间表“CategoryProduct”内联到“Category”表的方式,如下所示。

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import CategoryProduct, Category

class CategoryProductInline(admin.TabularInline):
    model = CategoryProduct
    min_num = 1 # 1 required inline field displayed
    extra = 2   # 2 unrequired inline fields displayed
    max_num = 5 # 5 inline fields as a maximum

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    inlines = [CategoryProductInline]

This is how it looks like:这是它的样子:

在此处输入图像描述

<The way with "ManyToManyField()"> <使用“ManyToManyField()”的方法>

This is the way with "ManyToManyField()" as shown below.这是“ManyToManyField()”的方法,如下所示。 The difference between the way with "ManyToManyField()" and the way without "ManyToManyField()" above is in this case of the way with "ManyToManyField()" , "categories" field with "ManyToManyField()" is added to "Product" class as you can see below and there is the only code difference between them so other things are the same.上面使用“ManyToManyField()”的方式和没有使用 ManyToManyField()”的方式之间的区别是在这种情况下使用“ManyToManyField()”的方式,带有 ManyToManyField()”“类别”字段被添加到“产品” “ class正如您在下面看到的,它们之间唯一的代码不同,所以其他都是一样的。

"models.py" : “模型.py”

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name
    
class Product(models.Model):
    name = models.CharField(max_length=255)
    categories = models.ManyToManyField(
        Category, 
        through='CategoryProduct'
    ) # "categories" field is added

    def __str__(self):
        return self.name

class CategoryProduct(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

    class Meta:
        unique_together = [['category', 'product']]

And you can add "products" field with "ManyToManyField()" to "Category" class and there is only code difference as well so other things are the same as well.您可以将带有“ManyToManyField()”“产品”字段添加到“类别”class ,并且只有代码不同,所以其他内容也相同。

"models.py" : “模型.py”

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class Category(models.Model):
    name = models.CharField(max_length=255)
    products = models.ManyToManyField(
        Product, 
        through='CategoryProduct'
    ) # "products" field is added

    def __str__(self):
        return self.name

class CategoryProduct(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)

    class Meta:
        unique_together = [['category', 'product']]

So, how you inline the middle table "CategoryProduct" to "Product" table is also the same as the way without "ManyToManyField()" as shown below.因此,将中间表“CategoryProduct”内联到“Product”表的方式也与没有“ManyToManyField()”的方式相同,如下所示。

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import CategoryProduct, Product

class CategoryProductInline(admin.TabularInline):
    model = CategoryProduct
    min_num = 1 
    extra = 2
    max_num = 5

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    inlines = [CategoryProductInline]

This is how it looks like:这是它的样子:

在此处输入图像描述

And, how you inline the middle table "CategoryProduct" to "Category" table is the same as the way without "ManyToManyField()" as shown below.并且,将中间表“CategoryProduct”内联到“Category”表的方式与没有“ManyToManyField()”的方式相同,如下所示。

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import CategoryProduct, Category

class CategoryProductInline(admin.TabularInline):
    model = CategoryProduct
    min_num = 1 # 1 required inline field displayed
    extra = 2   # 2 unrequired inline fields displayed
    max_num = 5 # 5 inline fields as a maximum

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    inlines = [CategoryProductInline]

This is how it looks like:这是它的样子:

在此处输入图像描述

In addition, when you use "ManyToManyField()" , you can remove the code for the middle table "CategoryProduct" which is customizable .此外,当您使用“ManyToManyField()”时,您可以删除可自定义的中间表“CategoryProduct”的代码 In this case you remove the code for the middle table "CategoryProduct" , the default middle table which is not customizable is created implicitly .在这种情况下,您删除中间表“CategoryProduct”的代码,隐式创建不可自定义的默认中间表 So, I removed the code for the middle table "CategoryProduct" and "through='CategoryProduct'" from "ManyToManyField()" as shown below.因此,我从“ManyToManyField()”中删除了中间表“CategoryProduct”“through='CategoryProduct'”的代码,如下所示。

"models.py" : “模型.py”

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=255)
    categories = models.ManyToManyField(Category)
    categories = models.ManyToManyField(
        Category, 
        # through='CategoryProduct'
    )

    def __str__(self):
        return self.name

# class CategoryProduct(models.Model):
#     category = models.ForeignKey(Category, on_delete=models.CASCADE)
#     product = models.ForeignKey(Product, on_delete=models.CASCADE)

#     class Meta:
#         unique_together = [['category', 'product']]

And by only registering "Product" as shown below, you can create the form of "Product" table with the inline of the default middle table .并且只需如下所示注册“Product” ,您就可以使用默认中间表的内联创建“Product”表的形式

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import Product

admin.site.register(Product)

This is how it looks like.这就是它的样子。 As you can see, the UI(User Interface) of "Categories" is different from when having the custom middle table "CategoryProduct" .如您所见, “类别”的 UI(用户界面)与具有自定义中间表“CategoryProduct”时不同。 For me, the UI of when having the custom middle table "CategoryProduct" is better:对我来说,拥有自定义中间表“CategoryProduct”时的 UI 更好:

在此处输入图像描述

And even if you register "Category" table as shown below.即使您注册“类别”表,如下所示。

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import Category

admin.site.register(Category)

The default middle table is not inlined to "Category" table as shown below:默认中间表未内联到“类别”表,如下所示:

在此处输入图像描述

To inline the default middle table to "Category" table , "Category" table must have ManyToManyField() as shown below.要将默认中间表内联到“类别”表“类别”表必须具有如下所示的ManyToManyField()

"models.py" : “模型.py”

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class Category(models.Model):
    name = models.CharField(max_length=255)
    products = models.ManyToManyField( # Here
        Product, 
        # through='CategoryProduct'
    )

    def __str__(self):
        return self.name

# class CategoryProduct(models.Model):
#     category = models.ForeignKey(Category, on_delete=models.CASCADE)
#     product = models.ForeignKey(Product, on_delete=models.CASCADE)

#     class Meta:
#         unique_together = [['category', 'product']]

Then, by only registering "Category" as shown below, you can create the form of "Category" table with the inline of the default middle table .然后,只需如下所示注册“Category” ,就可以使用默认中间表的内联创建“Category”表的形式

"admin.py" : “管理员.py”

from django.contrib import admin
from .models import Category

admin.site.register(Category)

This is how it looks like:这是它的样子:

在此处输入图像描述

This is how you create "many-to-many" relationship in Django .这就是您在Django中创建“多对多”关系的方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM