Django 如何将键外传给两个表之外的值?
我有以下型号:Django 如何将键外传给两个表之外的值?,django,django-models,django-queryset,Django,Django Models,Django Queryset,我有以下型号: class Ensemble(Group): name = models.CharField(max_length=100, null=True, blank=True) instrumentation = models.ForeignKey(Instrumentation -> Instrument, verbose_name=_('part'), related_name='ensemble_member', null=True, blank=True,
class Ensemble(Group):
name = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ForeignKey(Instrumentation -> Instrument, verbose_name=_('part'), related_name='ensemble_member', null=True, blank=True, on_delete=models.PROTECT)
class EnsembleMember(models.Model):
person = models.ForeignKey(Person, verbose_name=_('member'), on_delete=models.PROTECT)
instrument ???? = models.ForeignKey(Instrumentation, verbose_name=_('part'), related_name='ensemble_member', null=True, blank=True, on_delete=models.PROTECT) //This is the line in question
ensemble = models.ForeignKey(Ensemble, verbose_name=_('ensemble'), related_name='ensemble_member', on_delete=models.PROTECT)
class Instrumentation(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
class Instrument(MPTTModel):
name = models.CharField(max_length=100, null=True, blank=True)
category = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ManyToManyField(Instrumentation, verbose_name=_('instrumentation'), related_name='instrument', blank=True)
我希望能够将合奏成员与合奏下的乐器中可用的乐器联系起来。我将如何建立这种外键关系
例如:
有三种工具:
小提琴,
大提琴,和
钢琴
用这三种乐器演奏的一种乐器叫做“钢琴三重奏”
一个名为“Beaux Arts Trio”的合奏与乐器“Piano Trio”相连
“Menahem Pressler”是“Beaux Arts Trio”的合奏成员和钢琴家
我想把这个乐器和“钢琴”联系起来。钢琴是允许连接的乐器,因为它在与合奏相连的乐器中。如何在EnsembleMember模型中设置最后一个连接
在合奏和乐器演奏之前,我想进行以下连接
Piano Trio (Instrumentation) --> Beaux Arts Trio (Ensemble)
^ ^
| |
| |
Piano (Instrument) --> Menahem Pressler (pianist, Ensemble Member)
Violin (Instrument) --> Daniel Guilet (violinist, Ensemble Member)
Cello (Instrument) --> Bernard Greenhouse (cellist, Ensemble Member)
我想看看能不能帮你。我遇到的问题主要有两个
- Esemble成员梅纳赫姆·普雷斯勒在Beaux Arts三重奏中弹奏钢琴
- 合奏的Beaux Arts三重奏有乐器钢琴三重奏
- 可用的乐器是钢琴三重奏,由钢琴、大提琴、小提琴组成
- 音乐家/个人,名单上的是Menahem Pressler
- 对于钢琴,目前名单上有一位音乐家,梅纳赫姆·普雷斯勒
class Person(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return f'{self.name}'
class Instrument(models.Model): #MPTTModel):
name = models.CharField(max_length=100, null=True, blank=True)
category = models.CharField(max_length=100, null=True, blank=True)
person = models.ManyToManyField('Person', blank=True)
def __str__(self):
return f'{self.name}'
class Instrumentation(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
instrument = models.ManyToManyField('Instrument', blank=True)
def __str__(self):
return f'{self.name}'
class Ensemble(models.Model): # Group):
name = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ForeignKey(Instrumentation, null=True, blank=True, on_delete=models.PROTECT)
def __str__(self):
return f'{self.name}'
class EnsembleMember(models.Model):
person = models.ForeignKey(Person, on_delete=models.PROTECT)
instrument = models.ForeignKey(Instrument, null=True, blank=True, on_delete=models.PROTECT) # //This is the line in question
ensemble = models.ForeignKey(Ensemble, on_delete=models.PROTECT)
def __str__(self):
return f'{self.person}'
这个怎么样
class Ensemble(Group):
name = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ForeignKey(Instrumentation -> Instrument, verbose_name=_('part'), related_name='ensemble_member', null=True, blank=True, on_delete=models.PROTECT)
class EnsembleMember(models.Model):
person = models.ForeignKey(Person, verbose_name=_('member'), on_delete=models.PROTECT)
ensemble = models.ForeignKey(Ensemble, verbose_name=_('ensemble'), related_name='ensemble_member', on_delete=models.PROTECT)
ensemble_member = EnsemberMember.objects.first()
instrumentation = ensemble_member.ensemble.instrumentation
听起来您希望使用数据库约束来确保不会意外地将无效播放器的乐器添加到合奏中 我没有看到使用Django在数据库级别实现这一点的方法。如果您想进一步采用这种方法,可以查看Django 2.2+中的约束: 就我个人而言,我将为EnsembleMember模型定制模型管理器,以在应用程序代码级别验证约束 在视图代码中,而不是调用
EnsembleMember.objects.create(...)
…调用自定义模型管理器方法(具体示例请参见测试用例)
您的models.py文件如下所示(我替换了您的models.Model子类以使测试正常工作):
下面是一个演示您的用例的测试用例
from django.test import TestCase
from model_mommy import mommy
from instruments.models import (
Person,
Instrumentation,
Instrument,
Ensemble,
EnsembleMember,
InvalidInstrumentError,
)
class TestMrPressler(TestCase):
def test_mgr_constraint(self):
# Valid instruments
valid_instrumentation = mommy.make(Instrumentation)
piano = mommy.make(Instrument)
violin = mommy.make(Instrument)
cello = mommy.make(Instrument)
piano.instrumentation.add(valid_instrumentation)
violin.instrumentation.add(valid_instrumentation)
cello.instrumentation.add(valid_instrumentation)
# Invalid instrument
kazoo = mommy.make(Instrument)
beaux_arts_trio = mommy.make(
Ensemble,
instrumentation=valid_instrumentation
)
menahem_pressler = mommy.make(Person)
# Mr.Pressler does NOT play kazoo
with self.assertRaises(InvalidInstrumentError):
EnsembleMember.objects.add_member(
ensemble=beaux_arts_trio,
member=menahem_pressler,
instrument=kazoo
)
# But he fiddles like a beast
presslers_role = EnsembleMember.objects.add_member(
ensemble=beaux_arts_trio,
member=menahem_pressler,
instrument=violin
)
self.assertEqual(
presslers_role.instrument.name,
violin.name
)
您有什么理由不能将
仪器
和合奏
模型组合在一起吗?@DanielHolmes Instrument也将链接到其他模型。也许您需要提供更多上下文。根据当前显示的信息,它看起来有点过于复杂。@DanielHolmes Instrumentation模型将在应用程序的其他地方使用。在这里添加它将使问题更加复杂。这不是您在模型中强制执行的内容。相反,您可以在添加/更新这些项目的表单中执行此操作。
class Person(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
class Instrumentation(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
class Instrument(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
category = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ManyToManyField(
Instrumentation,
verbose_name='instrumentation',
related_name='instrument',
blank=True
)
class Ensemble(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
instrumentation = models.ForeignKey(
Instrumentation,
verbose_name='part',
related_name='ensemble_member',
null=True, blank=True,
on_delete=models.PROTECT
)
# Catch this error in the view
class InvalidInstrumentError(Exception):
pass
class EnsembleMemberManager(models.Manager):
# This custom manager enforces your business logic
def add_member(
self,
ensemble: 'Ensemble',
member: 'Person',
instrument: 'Instrument'
):
# This clause ensures that the new ensemble member plays a valid
# instrument for the ensemble's instrumentation
if not instrument in ensemble.instrumentation.instrument.all():
raise InvalidInstrumentError
# Just like EnsembleMember.objects.create
# use **kwargs if you have more properties
return self.create(
person=member,
instrument=instrument,
ensemble=ensemble
)
class EnsembleMember(models.Model):
person = models.ForeignKey(
Person,
verbose_name='member',
on_delete=models.PROTECT
)
# You want the ForeignKey to reference Instrument, not Instrumentation
# because the person plays a specific instrument and the Ensemble
# already references Instrumentation.
# Also updated the related_name so there is no duplication
instrument = models.ForeignKey(
Instrument,
verbose_name='part',
related_name='ensemble_member_instrument',
null=True,
blank=True,
on_delete=models.PROTECT
)
ensemble = models.ForeignKey(
Ensemble,
verbose_name='ensemble',
related_name='ensemble_member',
on_delete=models.PROTECT
)
# Add this line to use the custom manager.
objects = EnsembleMemberManager()
from django.test import TestCase
from model_mommy import mommy
from instruments.models import (
Person,
Instrumentation,
Instrument,
Ensemble,
EnsembleMember,
InvalidInstrumentError,
)
class TestMrPressler(TestCase):
def test_mgr_constraint(self):
# Valid instruments
valid_instrumentation = mommy.make(Instrumentation)
piano = mommy.make(Instrument)
violin = mommy.make(Instrument)
cello = mommy.make(Instrument)
piano.instrumentation.add(valid_instrumentation)
violin.instrumentation.add(valid_instrumentation)
cello.instrumentation.add(valid_instrumentation)
# Invalid instrument
kazoo = mommy.make(Instrument)
beaux_arts_trio = mommy.make(
Ensemble,
instrumentation=valid_instrumentation
)
menahem_pressler = mommy.make(Person)
# Mr.Pressler does NOT play kazoo
with self.assertRaises(InvalidInstrumentError):
EnsembleMember.objects.add_member(
ensemble=beaux_arts_trio,
member=menahem_pressler,
instrument=kazoo
)
# But he fiddles like a beast
presslers_role = EnsembleMember.objects.add_member(
ensemble=beaux_arts_trio,
member=menahem_pressler,
instrument=violin
)
self.assertEqual(
presslers_role.instrument.name,
violin.name
)