Taking all of the code in the previous examples, we end up with the following.
from datetime import datetime
from Typing import Optional
from pydantic import (
BaseModel,
Field.
field_validator,
ValidationError
)
class CheckIfInvoiceIsPaidRequest(BaseModel):
invoice_id: int = Field(alias="invoiceId")
@field_validator("invoice_id")
def invoice_id_is_more_than_zero(cls, invoice_id):
if invoice_id <= 0:
raise ValidationError("invoice_id must be more than zero")
return invoice_id
class InvoicePaymentStatusResponse(BaseModel):
invoice_id: int = Field(alias="invoiceId")
invoice_paid: bool = Field(alias="invoicePaid")
invoice_paid_date: Optional[datetime] = Field(alias="invoicePaidDate")
For comparison, a similar object without Pydantic would require much more code. The more code you write to handle generic validation the less readable your code ends up.
Self documenting code is about readability, intent, and context.
# Without Pydantic
import json
class CheckIfInvoiceIsPaidRequest:
def __init__(self, invoice_id: int, *args, **kwargs):
self.invoice_id = invoice_id
# request JSON
request_data = json.loads({"invoiceId": 123})
invoice_id = request_data["invoiceId"]
if type(invoice_id) == int and invoice_id > 0:
request = CheckIfInvoiceIsPaidRequest(invoice_id=invoice_id)