Django 多对多关系
在 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="+")
在管理页面:
问题是:
在 models.py 中,如何设置 products
和 category
之间的多对多关系(m2m),这样在管理页面上,就像图片中显示的那样,b2
(这个产品)会被标记为属于 a2
(这个类别)。
对于 [产品,类别] 的实现,任何建议都非常欢迎,谢谢。
附言:
我在 Django 方面还是个新手。抱歉我的英语不太好。
2 个回答
2022年3月更新:
在Django中,有两种方法可以创建多对多关系。一种方法不使用“ManyToManyField()”,另一种方法使用“ManyToManyField()”。首先,我会展示不使用“ManyToManyField()”的方法。
<不使用“ManyToManyField()”的方法>
要创建多对多关系,必须在另外两个表之间有一个中间表,这个中间表需要包含来自两个表的外键,并且这两个外键的组合必须是唯一的。所以,我创建了一个名为“CategoryProduct”的中间表,它位于“Category”和“Product”表之间,并且包含来自“Category”和“Product”表的外键,这些外键的组合是唯一的,如下所示。
“models.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']]
接下来,这是如何将中间表“CategoryProduct”嵌入到“Product”表中的,如下所示。
“admin.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]
效果如下:
接下来,这是如何将中间表“CategoryProduct”嵌入到“Category”表中的,如下所示。
“admin.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]
效果如下:
<使用“ManyToManyField()”的方法>
这是使用“ManyToManyField()”的方法,如下所示。使用“ManyToManyField()”和不使用“ManyToManyField()”的区别在于,在使用“ManyToManyField()”的情况下,“Product”类中添加了一个“categories”字段,这个字段使用了“ManyToManyField()”,你可以看到下面的代码,其他部分是相同的。
“models.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']]
你也可以在“Category”类中添加一个“products”字段,同样使用“ManyToManyField()”,代码的其他部分也是相同的。
“models.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']]
因此,如何将中间表“CategoryProduct”嵌入到“Product”表中的方法与不使用“ManyToManyField()”的方法是一样的,如下所示。
“admin.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]
效果如下:
同样,如何将中间表“CategoryProduct”嵌入到“Category”表中的方法也是与不使用“ManyToManyField()”的方法相同,如下所示。
“admin.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]
效果如下:
此外,当你使用“ManyToManyField()”时,可以去掉中间表“CategoryProduct”的代码,这个中间表是可定制的。在这种情况下,你去掉了中间表“CategoryProduct”的代码,系统会自动创建一个不可定制的默认中间表。所以,我去掉了中间表“CategoryProduct”的代码和“through='CategoryProduct'”,如下所示。
“models.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']]
只需注册“Product”,如下所示,就可以创建带有默认中间表的“Product”表单。
“admin.py”:
from django.contrib import admin
from .models import Product
admin.site.register(Product)
效果如下。可以看到,“Categories”的用户界面与使用自定义中间表“CategoryProduct”时不同。对我来说,使用自定义中间表“CategoryProduct”的用户界面更好:
即使你像下面这样注册“Category”表,
“admin.py”:
from django.contrib import admin
from .models import Category
admin.site.register(Category)
默认中间表没有嵌入到“Category”表,如下所示:
要将默认中间表嵌入到“Category”表,“Category”表必须有ManyToManyField(),如下所示。
“models.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']]
然后,只需注册“Category”,如下所示,就可以创建带有默认中间表的“Category”表单。
“admin.py”:
from django.contrib import admin
from .models import Category
admin.site.register(Category)
效果如下:
这就是在Django中创建多对多关系的方法。
问题在于你有两个 ManyToMany 字段。正如你所注意到的,当在其中一个字段中设置了关系时,另一个字段却没有。
解决方法很简单:去掉其中一个字段。你只需要在关系的一侧保留一个 ManyToManyField。Django 会自动让你访问另一侧。所以,如果你在 Product
模型中保留了 categories
字段,你可以通过 my_product.categories.all()
来获取与某个产品相关的所有分类;而通过 my_category.product_set.all()
可以获取属于某个分类的所有产品。
你还需要去掉 related_name="+"
这个属性:你可能是因为出现了冲突才加上这个属性的,这应该是一个提示。