2

My situation: The client needs to PUT a deep object graph to my Rest Framework 3-powered server, and there are lots of validation rules that need to be applied to the incoming data. For example, imagine incoming JSON like so (this is a contrived example):

{  // a sales order
    dateOrdered: '2015-02-04T13:23:00-800',
    customer: 5400,  // the customer ID
    items: [
        {
            sku: "TV100",  // the stock item ID
            quantity: 1,
        },
        {
            sku: "HDMI04",  // the stock item ID
            quantity: 3,
        }
    ]
}

And the following serializers:

class OrderSerializer(ModelSerializer):
    dateOrdered = DateTimeField()
    customer = PrimaryKeyRelatedField(queryset=Customer.objects.all())
    items = OrderItemSerializer(many=True)
    class Meta:
        model = Order
    def validate(self, data):
        # see my question below

class OrderItemSerializer(ModelSerializer):
    sku = PrimaryKeyRelatedField(queryset=Item.objects.all())
    quantity = IntegerField()
    class Meta:
        model = OrderItem

The validate() method will receive a Python dict that is a mix of dicts, lists, and Model objects, like so:

data = {
    'dateOrdered':  datetime.datetime(...)
    'customer':  <Customer.object at 0x....>
    'items': [
        {
            'sku': <Item.object at 0x....>
            'quantity': 1,
        },
        {
            'sku': <Item.object at 0x....>
            'quantity': 3,
        }
    ]
}

My problem: validating this "mixed" data structure is ugly (especially in my actual application, where the data structure is much deeper and more complex). I and future maintainers need to remember which "nodes" are Model objects vs dicts; the lists and dicts have no business logic methods to help with the validation; etc.

It would be much nicer to get the "final" object graph of Django models and just call clean() on that -- doing so would allow me to implement my validation logic in the models themselves, taking advantage of the object graph, allowing reuse of the validation logic in other contexts, etc.

My question: does anyone have any experience with complex validation like this, and if so, do you have any advice or best practices you can share?

Thanks!

2

1 Answer 1

0

I think you can try to create items in sub-serilizers:

class OrderItemSerializer(ModelSerializer):
    sku = PrimaryKeyRelatedField(queryset=Item.objects.all())
    quantity = IntegerField()
    class Meta:
        model = OrderItem

    def to_internal_value(self, data):
        item = OrderItem(sku=data['sku'], quantity=data['quantity'])
        item.clean()
        return {'item': item}

Then you will receive something like this:

data = {
    'dateOrdered':  datetime.datetime(...)
    'customer':  <Customer.object at 0x....>
    'items': [
        {
            'item': <OrderItem.object>
        },
        {
            'item': <OrderItem.object>
        }
    ]
}

Warning: new OrderItemSerializer is quite specific and can be used only as nested serializer.

Sign up to request clarification or add additional context in comments.

2 Comments

Interesting, this is worth investigating. In the meantime... are you needlessly wrapping each OrderItem in a dict? I'd like items to be a list of OrderItem instances.
Actually not. You can return OrderItem object as internal value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.