在Django REST Framework中使用ModelSerializer创建关联数据

时间:2018-05-16 03:38:25

标签: django django-rest-framework

我正在使用Django 2.0Django REST Framework编写REST API。

我的 contacts / models.py 包含

class Contact(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100, blank=True, null=True)
    date_of_birth = models.DateField(blank=True, null=True)
    avatar = models.ImageField(upload_to='contact/%Y/%m/%d', blank=True)

    class Meta:
        db_table = 'contacts'


class ContactPhoneNumber(models.Model):
    contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
    phone = models.CharField(max_length=100)

    class Meta:
        db_table = 'contact_phone_numbers'

contacts / serializers.py

class ContactPhoneNumberSerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactPhoneNumber
        fields = ('id', 'phone', 'primary', 'created', 'modified')


class ContactSerializer(serializers.HyperlinkedModelSerializer):
    phone_numbers = ContactPhoneNumberSerializer(source='contactphonenumber_set', many=True)
    url = serializers.HyperlinkedRelatedField(
        view_name='contacts:detail',
        read_only=True
    )

    class Meta:
        model = Contact
        fields = ('url', 'id', 'first_name', 'last_name', 'date_of_birth', 'avatar', 'phone_numbers')


    def create(self, validated_data):
        print(validated_data)
        instance = Contact.objects.create(**validated_data)
        instance.save()
        return instance

我希望能够与contact一起创建phone_number,并且一个联系人可以拥有多个电话号码。

但是当我发送只有联系人数据的POST请求时,它会出错

'contactphonenumber_set' is an invalid keyword argument for this function

仅在呼叫联系人时显示json响应中的所有关联手机号码,但无法创建记录。

print(validated_data)提供以下数据

{'first_name': 'Anshuman', 'last_name': 'Upadhyay', 'date_of_birth': datetime.date(2018, 5, 15), 'contactphonenumber_set': [], 'user_id': <SimpleLazyObject: <User: anuj>>}

如何使用REST Framework创建相关的多个字段?

2 个答案:

答案 0 :(得分:3)

您无法直接将contactphonenumber_set传递给objects.create()方法。您应该分别创建每个相关的语音,如下所示:

def create(self, validated_data):
        print(validated_data)
        phone_numbers = validated_data.pop('contactphonenumber_set')
        instance = Contact.objects.create(**validated_data)
        for phone_data in phone_numbers:
            ContactPhoneNumber.objects.create(contact=instance, **phone_data)
        return instance

查看有关可写嵌套序列化程序here的详细信息。

答案 1 :(得分:0)

为您提供drf-writable-nested

的演示

models.py:

class UnitGroup(models.Model):
    name = models.CharField(max_length=255,
                            verbose_name='名称')

class Unit(models.Model):
    unit_group = models.ForeignKey('medicine.UnitGroup',
                                   related_name='unit_unit_group',
                                   null=True,
                                   on_delete=models.SET_NULL,
                                   verbose_name='unit_group')
    name = models.CharField(max_length=255,
                            verbose_name='name')
    display_name = models.CharField(max_length=255,
                                    verbose_name='display_name')

serializers.py:

class UnitCreateSerializer(ModelSerializer):
    class Meta:
        model = Unit
        fields = ('name', 'display_name', 'ratio', 'is_active')

class UnitGroupCreateSerializer(WritableNestedModelSerializer):
    unit_unit_group = UnitCreateSerializer(many=True)

    class Meta:
        model = UnitGroup
        fields = ('unit_unit_group', 'name')

tests.py:

class UnitGroupTests(APITestCase):
    def setUp(self):
        try:
            self.user = User.objects.get(tel='18094213198')
        except User.DoesNotExist:
            self.user = User.objects.create_user(tel='18094213198', password='123456')
        self.user.user_permissions.add(*get_model_permission(UnitGroup))
        token, _ = Token.objects.get_or_create(user=self.user)
        self.access_token = token.access_token

    def test_create(self):
        """create"""
        url = reverse('unitgroup-list')
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.access_token)
        data = {'name': 'Widget', 'unit_unit_group': [{'name': '1', 'display_name': 'yi'}]}
        response = self.client.post(url, data, format='json')
        print([(x, x.unit_group) for x in Unit.objects.all()])
        print('create', json.dumps(response.data, ensure_ascii=False, indent=2))
        self.assertEqual(response.status_code, status.HTTP_200_OK)

测试输出:

[(<Unit: 1>, <UnitGroup: Widget>)]
create{
  "id": 1,
  "name": "Widget",
  "create_time": "2018-05-08 16:59:51",
  "update_time": "2018-05-08 16:59:51"
}