如何获取Django序列化程序以保存外键对象?

时间:2020-07-24 01:57:59

标签: python-3.x django django-models django-rest-framework django-serializer

我正在使用Python 3.8和Django3。我有以下模型。请注意,第二个具有第一个的外键...

class ContactMethod(models.Model):
    class ContactTypes(models.TextChoices):
        EMAIL = 'EMAIL', _('Email')
        PHONE = 'PHONE', _('Phone')

    type = models.CharField(
        null=False,
        max_length=5,
        choices=ContactTypes.choices,
    )
    phone = PhoneNumberField(null=True)
    email = models.EmailField(null=True)

    class Meta:
        unique_together = ('phone', 'email',)


class Coop(models.Model):
    objects = CoopManager()
    name = models.CharField(max_length=250, null=False)
    types = models.ManyToManyField(CoopType, blank=False)
    addresses = models.ManyToManyField(Address)
    enabled = models.BooleanField(default=True, null=False)
    phone = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_phone')
    email = models.ForeignKey(ContactMethod, on_delete=models.CASCADE, null=True, related_name='contact_email')
    web_site = models.TextField()

使用Django rest框架,我创建了以下序列化程序以帮助保存数据...

class ContactMethodPhoneSerializer(serializers.ModelSerializer):
    class Meta:
        model = ContactMethod
        fields = ['type', 'phone']
        read_only_fields = ['type']
        extra_kwargs = {'type': {'default': 'PHONE'}}


class CoopSerializer(serializers.ModelSerializer):
    types = CoopTypeSerializer(many=True, allow_empty=False)
    addresses = AddressTypeField(many=True)
    phone = ContactMethodPhoneSerializer()
    email = ContactMethodEmailSerializer()

    class Meta:
        model = Coop
        fields = '__all__'

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['types'] = CoopTypeSerializer(instance.types.all(), many=True).data
        rep['addresses'] = AddressSerializer(instance.addresses.all(), many=True).data
        return rep

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """

        coop_types = validated_data.pop('types', {})
        phone = validated_data.pop('phone', {})
        email = validated_data.pop('email', {})
        instance = super().create(validated_data)
        for item in coop_types:
            coop_type, _ = CoopType.objects.get_or_create(name=item['name'])
            instance.types.add(coop_type)
        instance.phone = ContactMethod.objects.create(type=ContactMethod.ContactTypes.PHONE, **phone)
        instance.email = ContactMethod.objects.create(type=ContactMethod.ContactTypes.EMAIL, **email)
        return instance

但是,在单元测试(实际)中,当我尝试像这样保存数据时

    serializer_data = {
        "name": name,
        "types": [
            {"name": coop_type_name}
        ],
        "addresses": [{
            "formatted": street,
            "locality": {
                "name": city,
                "postal_code": postal_code,
                "state": state.id
            }
        }],
        "enabled": enabled,
        "phone": {
          "phone": phone
        },
        "email": {
          "email": email
        },
        "web_site": web_site
    }

    serializer = CoopSerializer(data=serializer_data)
    assert serializer.is_valid(), serializer.errors
    coop_saved = serializer.save()
    coop = Coop.objects.get(pk=coop_saved.id)
    ...
    assert coop.phone.phone == phone

外键字段(电子邮件和电话)未保存(它们为null)。所有其他字段正确保存。为了成功保存外键字段,我还缺少什么?

1 个答案:

答案 0 :(得分:0)

因为您最后没有调用保存:

def create(self, validated_data):
    """
    Create and return a new `Snippet` instance, given the validated data.
    """

    coop_types = validated_data.pop('types', {})
    phone = validated_data.pop('phone', {})
    email = validated_data.pop('email', {})
    instance = super().create(validated_data)
    for item in coop_types:
        coop_type, _ = CoopType.objects.get_or_create(name=item['name'])
        instance.types.add(coop_type)
    instance.phone = ContactMethod.objects.create(type=ContactMethod.ContactTypes.PHONE, **phone)
    instance.email = ContactMethod.objects.create(type=ContactMethod.ContactTypes.EMAIL, **email)
    # call save here
    instance.save()
    return instance