3

EDIT: My original question refered to PUT requests, I have changed it to PATCH based on the answer provided by thecoshman.

I am developing a RESTful webservice using cornice and I have recently discovered colander. My question is related to PATCH requests. I now know PUT requests should be complete records but not so with PATCH requests. Can I use colander to validate json data attached to a PATCH request?

Colander is great for validating POST requests as it ensures I have all the right data in my json and also removes any extraneous data.

Here's my simple schema.

class OrganisationSchemaRecord(MappingSchema):
    orgname = SchemaNode(String())
    fullname = SchemaNode(String())
    description = SchemaNode(String(), missing=drop)

class OrganisationSchema(MappingSchema):
    organisation = OrganisationSchemaRecord()

This allows me to keep my view code simple like this.

@view(validators=(unique,), renderer='json', schema=OrganisationSchema)
def collection_post(self):
    """Adds a new organisation"""
    org = DBOrg(**self.request.validated['organisation'])#sqlalchemy model
    DBSession.add(org)
    return {'organisation': org}

The magic being schema=OrganisationSchema which validates the json body of the request and places is in self.request.validated['organisation'] as per the schema.

It also works great with my other validator which ensures the primary key is not already in use.

def unique(request):
    if 'organisation' in request.validated: #Implies a validated schema
        orgname = request.validated['organisation']['orgname']
        if DBSession.query(DBOrg).get(orgname):
            request.errors.add('url', 'orgname', 'This organisation already exists!')

However, if I want to process a PATCH request to update the fullname or description fields then validation fails unless the request also includes a value for orgname which I don't want to change.

What is the best solution? Do I insist on complete and valid records being PATCHed to the server, do I define a different schema or am I missing something?

Graeme Stuart
  • 5,837
  • 2
  • 26
  • 46

1 Answers1

2

honestly, skipped over most of the question, so hopefully I didn't miss anything too big.

Should PUT requests be full records - Yes, absolutely.

PUT requests put an entire replacement record at the URI you requested.

If you want to perform a partial modification, you should use PATCH (which is surprisingly lesser known). Before PATCH, the theory would be, GET the record, modify locally, PUT entire record back

thecoshman
  • 8,394
  • 8
  • 55
  • 77
  • You are right, so I think I need to update my question as I probably meant to refer to PATCH requests. – Graeme Stuart Jun 13 '14 at 20:48
  • If your server can only (as it currently seems) support full records, then accept them as PUT requests. You can then later work on supporting PATCH. – thecoshman Jun 14 '14 at 17:44
  • That is what I have done. I'm still interested in how to approach validate PATCH data with colander – Graeme Stuart Jun 16 '14 at 11:13
  • 1
    @GraemeStuart I'm guessing you need to have an alternate schema where each element is marked as `missing=colander.drop, default=colander.drop` so that missing elements don't trip the validation step. Then only the elements that exist will be validated and missing elements will be ignored. You probably also have to make sure than the `unknown` value on the `colander.Mapping` is set to `'raise'` so values that don't match an element of the schema cause an error. – Tim Tisdall Jul 29 '15 at 14:00
  • Thanks Tim. It seems a bit odd and not very DRY to have two similar schema for the same resource but I guess this is the only way. – Graeme Stuart Jul 29 '15 at 15:26
  • I believe having two schema is probably the only 'full proof' way. One schema serves to define what a full record is, it should be used when POSTing a brand new record, when PUTing an updated record and will be used when the client GETs a record (this 'create' schema could have optional elements though). Along with that, you would have a second schema that has (nearly) all elements optional, basically allow the client to send as little as they want, the server then works out how to update the current record. The second schema could not support some elements, thus making them immutable(ish). – thecoshman Jul 30 '15 at 07:49
  • ofc, what I said there was from more of a pure theoretical view. I know that with some of the JEE libs you can have a much looser definition of the data that can allow for an entity to be mapped to much more easily. Python might have similar, and what with rather loose data structures, you could consider no schema... probably not the best of ideas there though. – thecoshman Jul 30 '15 at 07:52