Search code examples
pythonvalidationreferential-integritycerberus

How to check referential integrity in Cerberus?


Consider the following Cerberus schema:

{
  'employee': {
    'type': 'list',
    'schema': {
      'type': 'dict',
      'schema': {
        'id': {'required': True, 'type': 'integer'},
        'name': {'required': True, 'type': 'string'}
      }
    }
  },
  'ceo-employee-id': {'required': True, 'type': 'integer'}
}

1) How can I validate that the ceo-employee-id matches one of the id values in the employee list? (Referential integrity)

2) How can I validate that each id in the employee list is unique (i.e. no duplicate employee ids)?

I realize I can do this at run-time after validating and parsing the config as suggested by @rafael below. I am wondering if I can do it with the Cerberus validation features.


Solution

  • You'll need to make use of a custom validator that implements check_with methods, use the document property in these, and amend your schema to include these:

    from cerberus import Validator
    
    
    class CustomValidator(Validator):
        def _check_with_ceo_employee(self, field, value):
            if value not in (x["id"] for x in self.document["employee"]):
                self._error(field, "ID is missing in employee list.")
    
        def _check_with_employee_id_uniqueness(self, field, value):
            all_ids = [x["id"] for x in self.document["employee"]]
            if len(all_ids) != len(set(all_ids)):
                self._error(field, "Employee IDs are not unique.")
    
    
    validator = CustomValidator({
        'employee': {
            'type': 'list',
            'schema': {
                'type': 'dict',
                'schema': {
                    'id': {'required': True, 'type': 'integer'},
                    'name': {'required': True, 'type': 'string'}
                 },
            },
            'check_with': 'employee_id_uniqueness'
        },
        'ceo-employee-id': {'required': True, 'type': 'integer', 'check_with': 'ceo_employee'}
    })
    

    The referenced document contains hints on all the parts used here.

    (I apologize for any indentation error that might have slipped into the example.)