{"id":23532642,"url":"https://github.com/outbrain-inc/valid_model","last_synced_at":"2025-07-26T17:12:17.352Z","repository":{"id":21443564,"uuid":"24761891","full_name":"outbrain-inc/valid_model","owner":"outbrain-inc","description":"valid_model ","archived":false,"fork":false,"pushed_at":"2023-03-31T12:21:05.000Z","size":39,"stargazers_count":9,"open_issues_count":0,"forks_count":4,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-29T19:34:31.513Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/outbrain-inc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-10-03T14:42:11.000Z","updated_at":"2024-11-11T18:34:02.000Z","dependencies_parsed_at":"2024-11-12T17:02:42.953Z","dependency_job_id":"435f19cb-f266-4e87-abc4-76d81a8103d2","html_url":"https://github.com/outbrain-inc/valid_model","commit_stats":null,"previous_names":["outbrain-inc/valid_model"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outbrain-inc%2Fvalid_model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outbrain-inc%2Fvalid_model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outbrain-inc%2Fvalid_model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/outbrain-inc%2Fvalid_model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/outbrain-inc","download_url":"https://codeload.github.com/outbrain-inc/valid_model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250337264,"owners_count":21414092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-25T23:12:10.352Z","updated_at":"2025-04-22T22:41:15.126Z","avatar_url":"https://github.com/outbrain-inc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"valid_model\n===========\n\nThe valid_model library is intended to allow declarative modeling of an object schema in a similar way to various ORM and form validation libraries while making as few assumptions as possible about the way it will be used.\n\nAttributes in a `valid_model.Object` instance are guaranteed to have all fields exist with defined restrictions and defaults available. In addition each field can have a mutator function applied to it to enforce uniformity.  An example of this could be make a `String` attribute always be uppercase.\n\nYou can nest `Object` classes inside one another using the `EmbeddedObject`, `Set`, `Dict`, and `List` descriptors\n\nThe available descriptors are in `valid_model.descriptors` and include:\n`Generic`, `String`, `Integer`, `Float`, `Bool`, `DateTime`, `TimeDelta`, `List`, `Set`, `Dict`, and `EmbeddedObject`\n\nWhen initializing an `Object` all initial values should be passed in as keyword arguments.\nWhen setting an `EmbeddedObject` attribute, it will automatically convert a `dict` to the appropriate `Object` subclass.\n\n`Object` instances have `Object.__json__` defined to be used as a hook to convert objects into `dict` for easy serialization.\n\n```python\nclass Person(Object):\n  name = String(nullable=False)\n  homepage = String()\n\nclass BlogPost(Object):\n  title = String(nullable=False, mutator=lambda x: x.title())\n  updated = DateTime(nullable=False, default=datetime.utcnow) # default to time object is created\n  published = DateTime() # default value will be None\n  author = EmbeddedObject(Person)\n  contributors = List(value=EmbeddedObject(Person))\n  tags = List(value=String(nullable=False))\n\n  def validate(self):\n    super(BlogPost, self).validate()\n    if self.published is not None and self.published \u003e self.updated:\n      raise ValidationError('a post cannot be published at a later date than it was updated')\n\npost = BlogPost(title='example post', author={'name': 'Josh'}, tags=['tag1', 'tag2'])\n\"\"\"\npost.__json__() would output\n{\n  'title': 'Example Post',\n  'author': {\n      'name': 'Josh',\n      'homepage': None\n   },\n   'contributors': [],\n   'updated': datetime(2014, 10, 6, 10, 23),\n   'published': None,\n   'tags': ['tag1', 'tag2']\n}\n\"\"\"\n```\n##Descriptor Options\n\n|Keyword Arg | Default | Description |\n|:-----------|--------|:------------------\n|nullable | `True`\u003csup\u003e1\u003c/sup\u003e | determines if it is valid for an attribute to be set to None\n|mutator | no mutator | allows some mutation to occur on the value. a common use case would be to format a string to always be uppercase\n|validator | no validator| a function which returns truthy if the value is valid\n|default | `None`\u003csup\u003e2\u003c/sup\u003e  | a scalar value or function which the attribute will be set to on object initialization if no value is specified at in the constuctor\n|value| no descriptor\u003csup\u003e3\u003c/sup\u003e | a descriptor to validate values in the container attribute\n| key | no descriptor\u003csup\u003e4\u003c/sup\u003e | a descriptor to validate keys in the container attribute\n\u003csup\u003e1\u003c/sup\u003e Not available on `Set`, `List`, and `Dict`. If an attribute with that descriptor is set to `None` it will actually set it to an empty instance of their respective types  \n\u003csup\u003e2\u003c/sup\u003e Container objects `Set`, `List`, and `Dict` initialize to an empty instance of their respective types  \n\u003csup\u003e3\u003c/sup\u003e Only available on `Dict`, `List`, and `Set`  \n\u003csup\u003e4\u003c/sup\u003e Only available on `Dict`  \n\n`EmbededObject` takes one argument which is the `Object` class that is being embedded.\n\n##How Validation Works\nValidation occurs whenever an attribute is set.\n\n1. Typechecking and any type coercion implemented occurs\n2. The value is checked if it is None  \n    * If it is None and nullable == False a ValidationError is raised otherwise it is set to None\n3. If mutator function is defined it will run\n4. If a validator function is defined it will run  \n    * A ValidationError is raised if the validator function returns falsey\n\n\n### Complex Validation\nIn addition to validators being defined on individual attributes there is a validate method on Object instances which may be overridden for more complicated validation logic that may include a combination of multiple fields.  By default it will just revalidate all attributes of an `Object` instance.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutbrain-inc%2Fvalid_model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foutbrain-inc%2Fvalid_model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foutbrain-inc%2Fvalid_model/lists"}