如何创建具有关系的 Django 模型,允许集合中的项目在该集合的容器中仅使用一次
How to create a Django Model with relationships allowing items from a set to be used just once within a container for that set
我想为 Django ORM 创建一个关系,我可以在其中添加一个集合中的对象,以及在该关系中关联的数据,但每个项目只添加一次到任何给定的容器。我的意思是使用术语集,定义如下:
A set is a well defined collection of distinct objects.
集合中的每个项目,SetItem,在集合中都是唯一的。我确保它们在这种情况下是唯一的,方法是在 class 定义中使用 unique=True
kwarg 定义其字段。这些项目的容器 SetItemContainer 与 SetItem 有关系,后者允许容器将某些数据与 SetItemRelationship 相关联。
这是一个多对多关系,但有一个扭曲,每个 SetItem 只能有一个 A
、B
、C
或 D
个人 SetContainer。使用披萨和浇头类比,每个披萨可以有任意数量的浇头,每个浇头可以有任意数量的披萨,但在这种情况下,任何一个披萨都不能多次添加浇头,即你不能添加两个"Anchovies" 任何单个披萨的配料(这就是 SetItemRelationship 中的 "data" 的用途)。
您可以在此应用的 models.py
中看到建议的模式
from django.db import models
class SetItem(models.Model):
text = models.CharField(max_length=32, unique=True)
def __unicode__(self):
return self.text
class SetContainer(models.Model):
label = models.CharField(max_length=64)
set_items = models.ManyToManyField(SetItem, through='SetItemRelationship')
def __unicode__(self):
return self.label
class SetItemRelationship(models.Model):
container = models.ForeignKey(SetContainer)
item = models.ForeignKey(SetItem)
data = models.PositiveSmallIntegerField()
def __unicode__(self):
return "{:s} contains {:s}".format(self.container.label, self.item.text)
这种模型关系允许我创建多个 SetContainer 对象,每个对象都有自己的 SetItem 对象实例以及与之关联的数据。但是,我想仅限于为每个关系添加一个实例。我试图使用以下 admin.py:
来解决这个问题
from django.contrib import admin
from .models import SetItem, SetContainer, SetItemRelationship
class SetInline(admin.TabularInline):
model = SetItemRelationship
class SetContainerAdmin(admin.ModelAdmin):
inlines = [SetInline]
admin.site.register(SetContainer, SetContainerAdmin)
admin.site.register(SetItem)
如您在下面的屏幕截图中所见,我能够将两个 SetItemRelationships 添加到具有 SetItem.text == A
的 Unique SetItem
如何设置关系以防止添加,理想情况下,在添加新的 SetItemRelationship 时防止下拉列表包含任何以前使用过的 SetItems。
编辑:我添加了一个解释性段落,包括比萨饼和配料的类比,并进行了修改以帮助解释这种关系。
在我看来,您需要的是使 container
和 item
成为 "unique together",这样相同的 container
就不会与相同的 [=12] 相关联=]不止一次。您可以使用 Meta
class:
class SetItemRelationship(models.Model):
container = models.ForeignKey(SetContainer)
item = models.ForeignKey(SetItem)
data = models.PositiveSmallIntegerField()
class Meta(object):
unique_together = (("container", "item"), )
def __unicode__(self):
return "{:s} contains {:s}".format(self.container.label, self.item.text)
一些想法:
# signals.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from app.models import SetItemRelationship
@receiver(pre_save, sender=SetItemRelationship)
def update_relationship_if_exists(sender, **kwargs):
new_relationship = kwargs['instance']
old_relationship = (SetItemRelationship.objects
.filter(container=instance.container, item=instance.item)
.first())
if old_relationship:
# force update
new_relationship.id = old_relationship.id
放置signals.py
的位置:link。
我想为 Django ORM 创建一个关系,我可以在其中添加一个集合中的对象,以及在该关系中关联的数据,但每个项目只添加一次到任何给定的容器。我的意思是使用术语集,定义如下:
A set is a well defined collection of distinct objects.
集合中的每个项目,SetItem,在集合中都是唯一的。我确保它们在这种情况下是唯一的,方法是在 class 定义中使用 unique=True
kwarg 定义其字段。这些项目的容器 SetItemContainer 与 SetItem 有关系,后者允许容器将某些数据与 SetItemRelationship 相关联。
这是一个多对多关系,但有一个扭曲,每个 SetItem 只能有一个 A
、B
、C
或 D
个人 SetContainer。使用披萨和浇头类比,每个披萨可以有任意数量的浇头,每个浇头可以有任意数量的披萨,但在这种情况下,任何一个披萨都不能多次添加浇头,即你不能添加两个"Anchovies" 任何单个披萨的配料(这就是 SetItemRelationship 中的 "data" 的用途)。
您可以在此应用的 models.py
中看到建议的模式from django.db import models
class SetItem(models.Model):
text = models.CharField(max_length=32, unique=True)
def __unicode__(self):
return self.text
class SetContainer(models.Model):
label = models.CharField(max_length=64)
set_items = models.ManyToManyField(SetItem, through='SetItemRelationship')
def __unicode__(self):
return self.label
class SetItemRelationship(models.Model):
container = models.ForeignKey(SetContainer)
item = models.ForeignKey(SetItem)
data = models.PositiveSmallIntegerField()
def __unicode__(self):
return "{:s} contains {:s}".format(self.container.label, self.item.text)
这种模型关系允许我创建多个 SetContainer 对象,每个对象都有自己的 SetItem 对象实例以及与之关联的数据。但是,我想仅限于为每个关系添加一个实例。我试图使用以下 admin.py:
来解决这个问题from django.contrib import admin
from .models import SetItem, SetContainer, SetItemRelationship
class SetInline(admin.TabularInline):
model = SetItemRelationship
class SetContainerAdmin(admin.ModelAdmin):
inlines = [SetInline]
admin.site.register(SetContainer, SetContainerAdmin)
admin.site.register(SetItem)
如您在下面的屏幕截图中所见,我能够将两个 SetItemRelationships 添加到具有 SetItem.text == A
如何设置关系以防止添加,理想情况下,在添加新的 SetItemRelationship 时防止下拉列表包含任何以前使用过的 SetItems。
编辑:我添加了一个解释性段落,包括比萨饼和配料的类比,并进行了修改以帮助解释这种关系。
在我看来,您需要的是使 container
和 item
成为 "unique together",这样相同的 container
就不会与相同的 [=12] 相关联=]不止一次。您可以使用 Meta
class:
class SetItemRelationship(models.Model):
container = models.ForeignKey(SetContainer)
item = models.ForeignKey(SetItem)
data = models.PositiveSmallIntegerField()
class Meta(object):
unique_together = (("container", "item"), )
def __unicode__(self):
return "{:s} contains {:s}".format(self.container.label, self.item.text)
一些想法:
# signals.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from app.models import SetItemRelationship
@receiver(pre_save, sender=SetItemRelationship)
def update_relationship_if_exists(sender, **kwargs):
new_relationship = kwargs['instance']
old_relationship = (SetItemRelationship.objects
.filter(container=instance.container, item=instance.item)
.first())
if old_relationship:
# force update
new_relationship.id = old_relationship.id
放置signals.py
的位置:link。