如何使用 manyToMany 和 inlineformset 在 Django 中表示家庭关系?

How to represent a family relationship in Django using manyToMany and inlineformset?

我想创建一个更新视图,其中 Person 模型将其相关的 FamilyMember 作为模板中的内联表单集。

在我的 model.py 中,我有:

class Person(models.Model):
    Name = models.CharField(max_length=50)

class FamilyMember(models.Model):
    person = models.ManyToManyField(Person, through='PersonFamilyMember')
    relationType= models.CharField(max_length=3, choices=FAMILYRELATION_CHOICE)

class PersonFamilyMember(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    related = models.ForeignKey(FamilyMember, on_delete=models.CASCADE)

在我的 form.py 中,我有:

class PersonForm(ModelForm):
    class Meta:
        model = Person
        fields = '__all__'

class FamilyMemberForm(ModelForm):
    class Meta:
        model = FamilyMember
        fields = '__all__'

RelatedFMFormSet = inlineformset_factory(Person, PersonFamilyMember.useCase.through,                                          form=FamilyMemberForm, can_delete=True, extra=1)

在我的 view.py 中,我有:

class PersonView(UpdateView):
    template_name = 'some.html'
    model = Person
    form_class = PersonForm

    def get_context_data(self, **kwargs):
        context = super(PersonView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['familymember_form'] = RelatedFMFormSet(self.request.POST, self.request.FILES,
                                                                   instance=self.object, prefix='relatedMember')
        else:
            context['familymember_form'] = RelatedFMFormSet(instance=self.object, prefix='relatedMember')
        return context

在我的模板中,我有:

<form method="POST" enctype="multipart/form-data" style="margin-left: 40px; margin-right: 40px">    
    {% for hidden_field in form.hidden_fields %}      
    {% endfor %}    
    {% csrf_token %}
    <fieldset>
        <legend>[Person Profile][1]</legend>
    {{ form.management_form }}
    {{ form.non_form_errors }}
    <div class="form-inline">       
            {% bootstrap_form  form %}
            </div>          
    </fieldset>

    <fieldset>
        <legend>Related Member(s)</legend>
        {{ familymember_form.management_form }}
        {{ familymember_form.non_form_errors }}
        <div class="formset-{{ relatedusecase_form.prefix }}">
            {% for hidden_field in familymember_form.hidden_fields %}
            {% endfor %}
            {% for relatedform in familymember_form.forms %}
                <div class="form-inline">
                    {% if relatedform.instance.pk %}
                        {{ relatedform.DELETE }}
                    {% endif %}
                    {{ relatedform }}
                </div>
            {% endfor %}
        </div>
        </fieldset>
<!--Other Code--!>
</form>

使用上面的代码段,当我渲染模板时,我得到(参见 link 1 中的图像:

因此,我无法单击相关下拉列表以 select 相关人员。另外,模板中也没有显示相关类型。

您需要两个模型,而不是 3 个,因为您实际上是将一个人与另一个人联系起来。

class Person(Model):
    name = ...
    family_members = ManyToManyField('self', through=FamilyMemberRelationship, through_fields=('person', 'relation'))

class FamilyMemberRelationship(Model):
    person = ForeignKey(Person, on_delete=CASCADE, related_name='relationships')
    related = ForeignKey(Person, on_delete=CASCADE, related_name='reverse_relationships')
    relation_type = CharField(max_length=3, choices=FAMILYRELATION_CHOICE)

请注意,该关系不是对称的。现在如果你有 phil 并且 james 是 Phil 的兄弟,那么你可以这样做:

relation = FamilyMemberRelationship(person=phil, related=james, relation_type='brother')
relation.save()
phil.family_members.all()  # james
phil.relations.filter(related=james).first().relation_type  # "brother" 
james.family_members.all()  # phil

这里唯一的问题是 relation_type 有一个方向(不对称),所以你不能:

james.relations.filter(related=phil).first()  # None

但你可以:

james.reverse_relations.filter(related=phil).first().relation_type  # "brother"

您可能还想保存一个 reverse_relation_type,这样如果 phil 是女孩,您就可以在那里放置 'sister'。