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)

我想看看能不能帮你。我遇到的问题主要有两个

  • 理想情况下,可以将一个工作示例剪切粘贴到django并运行,例如,什么是Group和MPPTModel
  • 复杂性是使用新的术语/概念增加的,比如合奏、乐器、乐器,我对此一无所知,所以首先我必须理解它是什么以及它是如何相互联系的
  • 下面是我的尝试以及我如何解释你的问题,你写道:“我希望能够将EnsembleMembers链接到Ensemble下的instrumentation中可用的乐器。我将如何创建这种外键关系。”

    i、 e.您希望将一个合奏成员(如Mannahem Pressler)链接到您在合奏中预定义的乐器之一。也就是说,如果合奏是钢琴三重奏,你有钢琴,现在你想把音乐家和这架钢琴联系起来

    开箱即用,我没有在Django Admin中使用编辑按钮来更改钢琴上的音乐家名称,但是您可以激活此按钮,请参阅此参考

    如有错误,请评论:

    • Esemble成员梅纳赫姆·普雷斯勒在Beaux Arts三重奏中弹奏钢琴
    • 合奏的Beaux Arts三重奏有乐器钢琴三重奏
    • 可用的乐器是钢琴三重奏,由钢琴、大提琴、小提琴组成
    • 音乐家/个人,名单上的是Menahem Pressler
    • 对于钢琴,目前名单上有一位音乐家,梅纳赫姆·普雷斯勒
    从django.db导入模型

    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
            )