【问题标题】:How to pass model instance to serializer?如何将模型实例传递给序列化程序?
【发布时间】:2021-05-04 09:53:13
【问题描述】:

我有一个看法:

def create(self, request):
        serializer = OrderPostSerializer(data=request.data)

        if serializer.is_valid():
            instance = serializer.save()
            serializer = OrderDetailsSerializer(instance)

            return AppResponse.success(
                "Order created successfully.",
                serializer.data,
                http_success_code=status.HTTP_201_CREATED,
            )

        return AppResponse.error(
            serializer.errors,
            None,
            http_error_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        )

序列化器:

class CustomerDetailsPostSerializer(serializers.Serializer):
    id = serializers.IntegerField(
        required=False, error_messages={"invalid": "id should be a valid integer."}
    )
    name = serializers.CharField(required=False, max_length=80)
    email = serializers.EmailField(required=False)
    mobile = serializers.CharField(
        required=False,
        max_length=15,
        error_messages={
            "max_length": "mobile field should not exceed more than 15 characters."
        },
    )
    company_name = serializers.CharField(
        required=False,
        max_length=255,
        allow_null=True,
        error_messages={
            "max_length": "company name field should not exceed more than 255 characters."
        },
    )

    def validate_email(self, value):
        customer = Customer.objects

        if self.instance:
            customer = customer.exclude(pk=self.instance.pk)

        if customer.filter(email__iexact=value):
            raise serializers.ValidationError(
                "Customer with this email already exists."
            )

        return value

    def validate_company(self, value):
        customer = Customer.objects

        if self.instance:
            customer = customer.exclude(pk=self.instance.pk)

        if customer.filter(company_name__iexact=value):
            raise serializers.ValidationError(
                "Customer with this company name already exists."
            )

        return value

    def validate_mobile(self, value):
        if validate_phone_number(value) is None:

            customer = Customer.objects

            if self.instance:
                customer = customer.exclude(pk=self.instance.pk)

            if customer.filter(mobile__iexact=value):
                raise serializers.ValidationError(
                    "Customer with this phone number already exists."
                )

        return value

    def validate(self, data):
        data = dict(data)
        if ("id" not in data.keys() or not data.get("id")) and (
            ("name" not in data.keys() or not data.get("name"))
            or ("email" not in data.keys() or not data.get("email"))
            or ("mobile" not in data.keys() or not data.get("mobile"))
        ):
            raise serializers.ValidationError(
                "Either id or name, email and mobile are required in customer details."
            )

        return data


class ContactDetailsPostSerializer(serializers.Serializer):
    id = serializers.IntegerField(
        required=False, error_messages={"invalid": "id should be a valid integer."}
    )
    name = serializers.CharField(required=False, max_length=80, allow_null=True)
    email = serializers.EmailField(required=False, allow_null=True)
    mobile = serializers.CharField(
        required=False,
        max_length=15,
        allow_null=True,
        error_messages={
            "max_length": "mobile field should not exceed more than 15 characters."
        },
    )

    def validate_email(self, value):
        contact = CustomerContact.objects

        if self.instance:
            contact = contact.exclude(pk=self.instance.pk)

        if contact.filter(email__iexact=value):
            raise serializers.ValidationError("Contact with this email already exists.")

        return value

    def validate_mobile(self, value):
        if validate_phone_number(value) is None:

            contact = CustomerContact.objects

            if self.instance:
                contact = contact.exclude(pk=self.instance.pk)

            if contact.filter(mobile__iexact=value):
                raise serializers.ValidationError(
                    "Contact with this phone number already exists."
                )

        return value

    def validate(self, data):
        data = dict(data)
        if (
            ("id" not in data.keys() or not data.get("id"))
            and ("name" not in data.keys() or not data.get("name"))
            and ("email" not in data.keys() or not data.get("email"))
            and ("mobile" not in data.keys() or not data.get("mobile"))
        ):
            return data

        raise serializers.ValidationError(
            "Either id or name, email and mobile are required in contact details."
        )


class DeliveryAddressDetailsPostSerializer(serializers.Serializer):
    id = serializers.IntegerField(
        required=False, error_messages={"invalid": "id should be a valid integer."}
    )
    title = serializers.CharField(required=False, max_length=100)
    street = serializers.CharField(required=False, max_length=255)
    suite = serializers.CharField(required=False, max_length=255)
    city = serializers.CharField(required=False, max_length=255)
    zipcode = serializers.CharField(required=False, max_length=10)
    county = serializers.CharField(required=False, max_length=255)
    state = serializers.CharField(required=False, max_length=255)

    def validate_title(self, value):
        address = Address.objects

        if self.instance:
            address = address.exclude(pk=self.instance.pk)

        if address.filter(title__iexact=value).filter(type=AddressType.DELIVERY):
            raise serializers.ValidationError(
                "Delivery address with this title already exists."
            )

        return value

    def validate(self, data):
        data = dict(data)
        if ("id" not in data.keys() or not data.get("id")) and (
            ("title" not in data.keys() or not data.get("title"))
            or ("street" not in data.keys() or not data.get("street"))
            or ("city" not in data.keys() or not data.get("city"))
            or ("zipcode" not in data.keys() or not data.get("zipcode"))
            or ("county" not in data.keys() or not data.get("county"))
            or ("state" not in data.keys() or not data.get("city"))
        ):
            raise serializers.ValidationError(
                "Either id or title, street, city, zipcode, county and state are required in delivery details."
            )

        return data


class BillingAddressDetailsPostSerializer(serializers.Serializer):
    id = serializers.IntegerField(
        required=False, error_messages={"invalid": "id should be a valid integer."}
    )
    title = serializers.CharField(required=False, max_length=100)
    street = serializers.CharField(required=False, max_length=255)
    suite = serializers.CharField(required=False, max_length=255)
    city = serializers.CharField(required=False, max_length=255)
    zipcode = serializers.CharField(required=False, max_length=10)
    county = serializers.CharField(required=False, max_length=255)
    state = serializers.CharField(required=False, max_length=255)
    payment_method = serializers.ChoiceField(required=False, choices=PaymentMethod)

    def validate_title(self, value):
        address = Address.objects

        if self.instance:
            address = address.exclude(pk=self.instance.pk)

        if address.filter(title__iexact=value).filter(type=AddressType.BILLING):
            raise serializers.ValidationError(
                "Billing address with this title already exists."
            )

        return value

    def validate(self, data):
        data = dict(data)
        if ("id" not in data.keys() or not data.get("id")) and (
            ("title" not in data.keys() or not data.get("title"))
            or ("street" not in data.keys() or not data.get("street"))
            or ("city" not in data.keys() or not data.get("city"))
            or ("zipcode" not in data.keys() or not data.get("zipcode"))
            or ("county" not in data.keys() or not data.get("county"))
            or ("state" not in data.keys() or not data.get("state"))
            # or ("payment_method" not in data.keys() or not data.get("payment_method"))
        ):
            raise serializers.ValidationError(
                "Either id or title, street, city, zipcode, county and state are required in billing details."
            )

        return data


class OrderPostSerializer(serializers.Serializer):
    customer_details = CustomerDetailsPostSerializer()
    delivery_address = DeliveryAddressDetailsPostSerializer()
    # billing_same_as_delivery = serializers.ChoiceField(
    #     required=True, choices=BillingSameAsDelivery
    # )
    billing_address = BillingAddressDetailsPostSerializer()
    contact_details = ContactDetailsPostSerializer()
    dumpster_type = serializers.CharField(
        required=False,
        allow_null=True,
        allow_blank=True,
        error_messages={"required": "Dumpster type is required."},
    )
    order_type = serializers.CharField(
        required=True, error_messages={"required": "Order type is required."}
    )
    delivery_date = serializers.DateField(
        required=True,
        error_messages={
            "required": "Delivery date is required.",
            "invalid": "Delivery date has wrong format. Use one of these formats instead: YYYY-MM-DD.",
        },
    )
    preferred_time = serializers.ChoiceField(
        required=False, choices=PreferredTime.choices, allow_null=True
    )
    renting_till = serializers.DateField(
        required=False,
        error_messages={
            "invalid": "Renting till date has wrong format. Use one of these formats instead: YYYY-MM-DD.",
        },
        allow_null=True,
    )
    dumpster_cost = serializers.DecimalField(
        required=True,
        max_digits=8,
        decimal_places=2,
        error_messages={"required": "Dumpster cost is required."},
    )
    city_tax = serializers.DecimalField(
        required=False, max_digits=8, decimal_places=2, allow_null=True
    )
    permit_tax = serializers.DecimalField(
        required=False, max_digits=8, decimal_places=2, allow_null=True
    )

    class Meta:
        # model = Order
        exclude = []

    def validate(self, data):
        if data["renting_till"] and data["renting_till"] < data["delivery_date"]:
            raise serializers.ValidationError(
                "Renting till date should be greater than delivery date."
            )

        return data

    def create(self, validated_data):
        try:
            with transaction.atomic():
                # attach existing customer or create new customer
                customer_details = validated_data.pop("customer_details")
                customer = None
                if "id" in customer_details.keys() and customer_details.get("id"):
                    try:
                        customer = Customer.objects.get(pk=customer_details.get("id"))
                    except Customer.DoesNotExist:
                        raise serializers.ValidationError("Invalid customer.")
                else:
                    customer = Customer.objects.create(
                        name=customer_details.get("name"),
                        email=customer_details.get("email"),
                        mobile=customer_details.get("mobile"),
                        company_name=customer_details.get("company_name"),
                    )

                # attach existing contact or create new contact
                contact_details = validated_data.pop("contact_details")
                contact = None
                if "id" in contact_details.keys() and contact_details.get("id"):
                    try:
                        contact = CustomerContact.objects.get(
                            pk=contact_details.get("id")
                        )
                    except CustomerContact.DoesNotExist:
                        raise serializers.ValidationError("Invalid contact.")
                elif (
                    "name" in contact_details.keys() and contact_details.get("name")
                ) and (
                    "mobile" in contact_details.keys() and contact_details.get("mobile")
                ):
                    contact = CustomerContact.objects.create(
                        name=contact_details.get("name"),
                        email=contact_details.get("email"),
                        mobile=contact_details.get("mobile"),
                        customer=customer,
                    )

                # attach existing delivery address or create new delivery address
                delivery_address_details = validated_data.pop("delivery_address")
                delivery_address = None
                if "id" in delivery_address_details.keys() and delivery_address_details.get(
                    "id"
                ):
                    try:
                        delivery_address = (
                            Address.objects.filter(type=AddressType.DELIVERY)
                            .filter(customer=customer)
                            .filter(pk=delivery_address_details.get("id"))
                        )
                    except Address.DoesNotExist:
                        raise serializers.ValidationError("Invalid delivery address.")
                else:
                    county = None
                    try:
                        county = County.objects.get(
                            name=delivery_address_details.get("county")
                        )
                    except County.DoesNotExist:
                        raise serializers.ValidationError("Invalid county.")

                    delivery_address = Address.objects.create(
                        customer=customer,
                        title=delivery_address_details.get("title"),
                        street=delivery_address_details.get("street"),
                        suite=delivery_address_details.get("suite"),
                        city=delivery_address_details.get("city"),
                        zipcode=delivery_address_details.get("zipcode"),
                        county=county,
                        state=delivery_address_details.get("state"),
                        type=AddressType.DELIVERY,
                    )

                # attach existing billing address or create new billing address
                billing_address_details = validated_data.pop("billing_address")
                billing_address = None
                if "id" in billing_address_details.keys() and billing_address_details.get(
                    "id"
                ):
                    try:
                        billing_address = (
                            Address.objects.filter(type=AddressType.BILLING)
                            .filter(customer=customer)
                            .filter(pk=billing_address_details.get("id"))
                        )
                    except Address.DoesNotExist:
                        raise serializers.ValidationError("Invalid billing address.")
                else:
                    county = None
                    try:
                        county = County.objects.get(
                            name=delivery_address_details.get("county")
                        )
                    except County.DoesNotExist:
                        raise serializers.ValidationError("Invalid county.")

                    if validated_data.get("billing_same_as_delivery"):
                        try:
                            billing_address = (
                                Address.objects.filter(type=AddressType.BILLING)
                                .filter(customer=customer)
                                .filter(title=delivery_address.title)
                            )
                        except Address.DoesNotExist:
                            billing_address = Address.objects.create(
                                customer=customer,
                                title=delivery_address.get("title"),
                                street=delivery_address.get("street"),
                                suite=delivery_address.get("suite"),
                                city=delivery_address.get("city"),
                                zipcode=delivery_address.get("zipcode"),
                                county=county,
                                state=delivery_address.get("state"),
                                payment_method=PaymentMethod.COD,
                            )
                    else:
                        billing_address = Address.objects.create(
                            customer=customer,
                            title=billing_address_details.get("title"),
                            street=billing_address_details.get("street"),
                            suite=billing_address_details.get("suite"),
                            city=billing_address_details.get("city"),
                            zipcode=billing_address_details.get("zipcode"),
                            county=county,
                            state=billing_address_details.get("state"),
                            payment_method=billing_address_details.get(
                                "payment_method"
                            ),
                        )

                dumpster_type = None
                if validated_data.get("dumpster_type"):
                    try:
                        dumpster_type = DumpsterType.objects.get(
                            name=validated_data.get("dumpster_type")
                        )
                    except DumpsterType.DoesNotExist:
                        raise serializers.ValidationError("Invalid dumpster type.")

                order_type = None
                try:
                    order_type = OrderType.objects.get(
                        name=validated_data.get("order_type")
                    )
                except OrderType.DoesNotExist:
                    raise serializers.ValidationError("Invalid order type.")

                # create order instance
                instance = Order.objects.create(
                    customer=customer,
                    contact=contact,
                    delivery_address=delivery_address,
                    billing_address=billing_address,
                    dumpster_type=dumpster_type,
                    order_type=order_type,
                    delivery_date=validated_data.get("delivery_date"),
                    preferred_time=validated_data.get("preferred_time"),
                    renting_till=validated_data.get("renting_till"),
                    dumpster_cost=validated_data.get("dumpster_cost"),
                    city_tax=validated_data.get("city_tax"),
                    permit_tax=validated_data.get("permit_tax"),
                )

            return instance
        except DatabaseError as database_error:
            print(database_error)
            raise serializers.ValidationError(database_error)

我正在 OrderPostSerializer 的 create() 方法中为客户模型创建一个实例。创建客户实例时,我想将该实例传递给 DeliveryAddressDetailsPostSerializer() 以便我可以检查是否存在与客户具有相同标题的送货地址。

我怎样才能做到这一点?

【问题讨论】:

    标签: django django-models django-rest-framework django-serializer


    【解决方案1】:

    我不确定你的模型是什么样的,但听起来你可以在序列化程序中使用Nested relationships你。这将使您的视图更简单,您不必担心将实例传递给下一个序列化程序。

    如果你有一个嵌套的序列化器,rest_framework 不支持写入。因此,您必须为create() 编写自定义方法(文档中的示例)。或use drf-writable-nested

    【讨论】:

      猜你喜欢
      • 2019-09-17
      • 2021-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2016-10-03
      • 2019-07-25
      • 1970-01-01
      相关资源
      最近更新 更多