如何在 Django REST 中通过多对多发布模型

Posted

技术标签:

【中文标题】如何在 Django REST 中通过多对多发布模型【英文标题】:How to POST Model with Many to Many through in Django REST 【发布时间】:2018-09-13 00:10:30 【问题描述】:

我有一个具有多对多连接的模型。我想让这个模型在 Django REST 中可用。默认情况下,这样的模型是只读的,但我也想写。此外,将直通连接的信息作为嵌套模型集成到 GET 中会很棒。

...
class KeyDateCase(models.Model):
    ...
    diagnoses_all_icd_10 = models.ManyToManyField(
        'ICD10', through='CaseICD10Connection')
...

class CaseICD10Connection(models.Model):
    case = models.ForeignKey('KeyDateCase', on_delete=models.CASCADE)
    icd_10 = models.ForeignKey('ICD10', on_delete=models.CASCADE)
    is_primary = models.BooleanField(default = False)
    certainty = models.CharField(
        max_length=1,
        choices=CERTAINTY_CHOICES,
        default='G',
    )

class ICD10(models.Model):

    primary_key_number = models.CharField(max_length=10, primary_key=True)

    star_key_number = models.CharField(max_length=10, blank=True, null=True)

    additional_key_number = models.CharField(
        max_length=10, blank=True, null=True)

    preferred_short_description = models.CharField(max_length=128, )
...

class KeyDateCaseViewSet(viewsets.ModelViewSet):
    ???

class KeyDateCaseSerializer(serializers.ModelSerializer):
    ???

我怎样才能做到这一点?我的视图和序列化程序应该是什么样的?

【问题讨论】:

你想如何显示diagnoses_all_icd_10,是ids列表还是你想显示整个对象? diagnoses_all_icd_10 = serializers.PrimaryKeyRelatedField( many=True, queryset= ICD10.objects.all(), write_only=True, required=False) 你可以定义类似的东西,你必须重写序列化程序创建方法并从验证数据中弹出 id,例如 icd_10 = valid_data.pop('diagnoses_all_icd_10')然后将其添加到 icd_10 中 icd 的模型实例```:instance.diagnoses_all_icd_10.add(ICD10.objects.get(pk=icd_10))``` 我建议在使用manytomanyfield 时使用views.py 中的类,您可以查看inlineformset_factory 中的forms.py 以及jquery-formset 中的前端。 【参考方案1】:

关于创建或更新嵌套对象,the documentation actually has a great example。如果可以的话,我会给你一个更好的。如果示例中有任何令人困惑的地方,很乐意在这里解释。

如果您采用这种方法,您的GET 请求将自动为您展开嵌套对象。

【讨论】:

【参考方案2】:

通常我通过POSTthrough 表的间接方式解决方法并实现嵌套创建()。如果我的回答不准确,请提供更多信息。

models.py

from django.db import models


class ICD10(models.Model):
    primary_key_number = models.CharField(max_length=10, primary_key=True)
    star_key_number = models.CharField(max_length=10, blank=True, null=True)
    additional_key_number = models.CharField(max_length=10, blank=True, null=True)
    preferred_short_description = models.CharField(max_length=128, )

    def __str__(self):
        return f'self.primary_key_number self.star_key_number'


class CaseICD10Connection(models.Model):
    case = models.ForeignKey('KeyDateCase', related_name='connections', related_query_name='key_date_cases', on_delete=models.CASCADE)
    icd_10 = models.ForeignKey('ICD10', related_name='connections', related_query_name='icd_10s', on_delete=models.CASCADE)
    is_primary = models.BooleanField(default=False)
    certainty = models.CharField(max_length=1, default='G', )


class KeyDateCase(models.Model):
    name = models.CharField(max_length=20)
    diagnose_all_icd_10 = models.ManyToManyField(ICD10, related_name='icd10s', related_query_name='icd10s',
                                                 through=CaseICD10Connection)

serializers.py

from rest_framework import serializers

from keydatecases.models import KeyDateCase, ICD10, CaseICD10Connection


class KeyDateCaseSerializer(serializers.ModelSerializer):
    class Meta:
        model = KeyDateCase
        fields = [
            'id',
            'name',
            'diagnose_all_icd_10',
        ]
        read_only_fields = ['id', 'diagnose_all_icd_10']


class ICD10Serializer(serializers.ModelSerializer):
    class Meta:
        model = ICD10
        fields = [
            'primary_key_number',
            'star_key_number',
            'additional_key_number',
            'preferred_short_description',
        ]


class CaseICD10ConnectionSerializer(serializers.ModelSerializer):
    case = KeyDateCaseSerializer()
    icd_10 = ICD10Serializer()

    class Meta:
        model = CaseICD10Connection
        fields = [
            'case',
            'icd_10',
            'is_primary',
            'certainty',
        ]

    def create(self, validated_data) -> CaseICD10Connection:
        # import ipdb;
        # ipdb.set_trace()
        # create key_date_case
        key_date_case = KeyDateCase.objects.create(**validated_data.get('case'))

        # create icd10
        icd10 = ICD10.objects.create(**validated_data.get('icd_10'))

        # create connection
        conn = CaseICD10Connection.objects.create(
            case=key_date_case, icd_10=icd10, is_primary=validated_data.get('is_primary'),
            certainty=validated_data.get('certainty')
        )
        return conn

viewsets.py

from rest_framework import viewsets

from keydatecases.api.serializers import CaseICD10ConnectionSerializer
from keydatecases.models import CaseICD10Connection


class CaseICD10ConnectionViewSet(viewsets.ModelViewSet):
    permission_classes = ()
    queryset = CaseICD10Connection.objects.all()
    serializer_class = CaseICD10ConnectionSerializer

我的存储库: 我与许多问题共享我的存储库。请不要介意。https://github.com/elcolie/tryDj2

【讨论】:

以上是关于如何在 Django REST 中通过多对多发布模型的主要内容,如果未能解决你的问题,请参考以下文章

在 Django Rest 框架 URL 中通过唯一 ID 但不是 PK 获取详细信息

在 Laravel 中通过多对多关系模型对 eloquent 模型进行排序

如何通过多对一关系中同一相关对象的两个属性在 django 中进行过滤?

如何在 django-rest-framework 中对权限进行单元测试?

如何通过多对多关系获取 django 模型?

Vue结合Django-Rest-Framework实现登录认证(上)