Chhatrapati Shahu Ji Maharaj University Students: Data Validation in Python APIs With Pydantic Exercises

Facebook
Twitter
LinkedIn
WhatsApp
Email

Data validation in a Python API means checking every incoming value against explicit rules before your code uses it, and returning a clear error when a value fails. Pydantic does this through typed models, so you describe the shape of valid data once and get parsing, constraints and structured error messages for free. The graded exercises below take you from a simple model to custom validators and error formatting, with an answer key that explains each choice.

Why validation belongs at the edge of your API

An API receives text from strangers: browsers, mobile apps, scripts and sometimes attackers. If you trust that text, bugs show up deep inside your code as a KeyError, a TypeError or a corrupted database row. If you validate at the boundary, bad data is rejected immediately and the client learns exactly what to fix.

Three jobs are involved, and it helps to keep them separate:

  • Parsing: turning raw JSON into Python types (for example, the string "25" into the integer 25 if you allow that).
  • Validating: enforcing rules such as range, length, format and allowed values.
  • Reporting: telling the caller which field failed, why, and with what status code.

Pydantic handles the first two and gives you structured data for the third. FastAPI, which is built on Pydantic, turns validation failures into HTTP 422 responses automatically. The exercises assume Pydantic v2 (BaseModel, Field, field_validator, model_validator). Syntax differs in v1, so check your installed version with pip show pydantic.

Setup for the Pydantic exercise set

You need Python 3.9 or newer, a virtual environment and two packages. Install them with pip install pydantic fastapi uvicorn. For Levels 1 to 3 you only need a plain Python file and Pydantic. Add FastAPI from Level 4 onward.

Work in a single file per level. Write your own answer first, run it yourself, and only then read the key. Everything in this article is illustrative; nothing here has been executed on your machine, so your own test run is the real check.

Free 2-Week CareerSwami Internship

Online, 14 days, with a free offer letter and a free completion certificate once you finish the work. Check with your college that it fits your requirement before you enrol.

Apply for the free internship

Graded scorecard: what each level trains

Level Exercise focus Pydantic features Target time
1 Typed models and required fields BaseModel, type hints, Optional, defaults 30 min
2 Constraints on values Field with ge, le, min_length, pattern; Literal 45 min
3 Custom rules field_validator, model_validator 60 min
4 Bad requests in an endpoint FastAPI, 422 handling, HTTPException 60 min
5 Useful error responses Custom exception handler, ValidationError.errors() 75 min
6 Nested data and lists Nested models, list[Model], strict mode 75 min

Score yourself per exercise: 2 points if it works and rejects the bad cases, 1 point if it works but misses an edge case, 0 if it fails. A full set is worth 24 points across the 12 exercises.

Level 1 and 2 exercises: models, types and constraints

Level 1: typed models

  1. Exercise 1.1. Create a Student model with name: str, age: int and email: str. Instantiate it with a valid dictionary, then with age="abc". Print the exception.
  2. Exercise 1.2. Add an optional phone field that defaults to None, and a is_active field that defaults to True. Show that omitting both still works but omitting name fails.

Level 2: constraints

  1. Exercise 2.1. Constrain age to 16 to 100, name to 2 to 60 characters, and roll_no to the pattern of two letters followed by six digits.
  2. Exercise 2.2. Add a course field that accepts only "BCA", "BCom" or "BSc" using Literal. Test with "MBA".
  3. Exercise 2.3. Create a Product model where price must be greater than 0 and quantity must be a non-negative integer. Test with price=0 and quantity=-1.

Level 3 to 6 exercises: custom rules, endpoints and error design

Level 3: custom validators

  1. Exercise 3.1. Write a field_validator that strips whitespace from name, rejects an empty result, and title-cases it.
  2. Exercise 3.2. Create a SignUp model with password and confirm_password. Use a model_validator to reject mismatches and passwords shorter than 8 characters without a digit.

Level 4: bad requests in an endpoint

  1. Exercise 4.1. Build POST /students that accepts your Student model. Send a body with a missing field using curl or the FastAPI /docs page and read the 422 response.
  2. Exercise 4.2. Add a business rule outside Pydantic: reject a duplicate roll_no with HTTPException(status_code=409). Explain in a comment why this is 409 and not 422.

Level 5: useful errors

  1. Exercise 5.1. Register a handler for RequestValidationError that returns JSON shaped like {"error": "validation_failed", "fields": [{"field": "age", "message": "..."}]}.

Level 6: nested data

  1. Exercise 6.1. Model an Order containing a customer (nested model) and a list of items with at least one entry. Send an item with a negative quantity and find the exact error location.
  2. Exercise 6.2. Turn on strict mode for quantity so that the string "3" is rejected instead of silently converted. Decide when each behaviour is appropriate.

Answer key with the reasoning behind each validation choice

Treat these as illustrative reference solutions. Variations are fine as long as they reject the same bad inputs.

Answer key, Levels 1 and 2

from typing import Optional, Literal
from pydantic import BaseModel, Field

class Student(BaseModel):
    name: str = Field(min_length=2, max_length=60)
    age: int = Field(ge=16, le=100)
    email: str
    roll_no: str = Field(pattern=r"^[A-Za-z]{2}\d{6}$")
    course: Literal["BCA", "BCom", "BSc"]
    phone: Optional[str] = None
    is_active: bool = True
  • 1.1: age="abc" raises a ValidationError with type int_parsing. Pydantic does not crash with a ValueError from your own code; it collects every failing field in one error object, which is why clients can fix all problems in a single round trip.
  • 1.2: A field with a default is optional; a field without one is required. Optional[str] = None says the value may be null and may be absent. Those are two separate ideas, and mixing them up is a frequent source of bugs.
  • 2.1: ge/le are inclusive bounds; gt/lt are exclusive. Use inclusive bounds for ages and exclusive for “must be positive”. The pattern uses anchors (^ and $) so that extra characters cannot hide around a valid core.
  • 2.2: Literal gives you an allow-list. An allow-list is safer than checking for bad values, because you cannot anticipate every bad value.
  • 2.3: Use Field(gt=0) for price and Field(ge=0) for quantity. For money in real systems, prefer Decimal over float to avoid rounding surprises.

Answer key, Levels 3 and 4

from pydantic import BaseModel, field_validator, model_validator

class SignUp(BaseModel):
    name: str
    password: str
    confirm_password: str

    @field_validator("name")
    @classmethod
    def clean_name(cls, v: str) -> str:
        v = v.strip()
        if not v:
            raise ValueError("name must not be blank")
        return v.title()

    @model_validator(mode="after")
    def check_passwords(self):
        if self.password != self.confirm_password:
            raise ValueError("passwords do not match")
        if len(self.password) < 8 or not any(c.isdigit() for c in self.password):
            raise ValueError("password needs 8+ characters and a digit")
        return self
  • 3.1: A field validator sees one value, so it fits cleaning and single-field rules. It must return the value, otherwise the field becomes None. Raise ValueError (not a custom crash) so Pydantic wraps it in its error report.
  • 3.2: A model validator runs after all fields are parsed, so it can compare two fields. Cross-field rules must live here, because a field validator cannot reliably see sibling values.
  • 4.1: FastAPI returns 422 Unprocessable Entity with a detail list holding loc, msg and type. The loc path such as ["body", "age"] tells you where the fault is.
  • 4.2: The body is well-formed but conflicts with existing state, so 409 Conflict fits. 422 means the payload itself is invalid. Pydantic cannot know about your database, so uniqueness checks belong in your service code.

Answer key, Levels 5 and 6

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def handle_validation(request: Request, exc: RequestValidationError):
    fields = [
        {"field": ".".join(str(p) for p in e["loc"][1:]),
         "message": e["msg"]}
        for e in exc.errors()
    ]
    return JSONResponse(status_code=422,
                        content={"error": "validation_failed", "fields": fields})
  • 5.1: Dropping the first loc element removes the word “body”, which is noise to a client. Keep one stable error code (validation_failed) so front-end code can branch on it, and keep the human message separate. Avoid echoing raw input values for password fields.
  • 6.1: For a bad item the location looks like ["body", "items", 0, "quantity"]. The integer is the list index, which lets a client highlight the exact row. Add Field(min_length=1) to items to refuse empty orders.
  • 6.2: Strict mode (Field(strict=True) or ConfigDict(strict=True)) refuses type coercion. Lax mode is friendlier for form data and query strings, where everything arrives as text. Strict mode suits internal service-to-service JSON where a string where a number belongs signals a real bug.

Worked illustrative example: grading one bad request end to end

Suppose a client sends this body to POST /students (illustrative input):

{"name": " ", "age": 14, "email": "ravi@example.com",
 "roll_no": "ab12", "course": "MBA"}

With the Level 2 model, you would expect four reported problems in a single response: the name is too short, the age is below 16, the roll number does not match the pattern and the course is not in the allowed list. Under the Level 5 handler, the response would resemble:

{"error": "validation_failed",
 "fields": [
  {"field": "name", "message": "String should have at least 2 characters"},
  {"field": "age", "message": "Input should be greater than or equal to 16"},
  {"field": "roll_no", "message": "String should match pattern ..."},
  {"field": "course", "message": "Input should be 'BCA', 'BCom' or 'BSc'"}
 ]}

Exact message wording varies by Pydantic version, so run it yourself and compare. Score yourself: if all four fields appear, you earn full marks for this scenario. If only the first error appears, you are probably raising exceptions manually instead of letting Pydantic collect them.

Mapping the exercises onto a 14-day internship plan

The free two-week online internship from CareerSwami runs 14 days, with a free offer letter and a free completion certificate after successful completion. If you pick a backend track, this exercise set fits that window well.

Days Activity Output to keep
1-2 Setup, Level 1 and 2 exercises Working Student and Product models
3-4 Level 3 custom validators; write 10 pass/fail test inputs A test file with expected outcomes
5-6 Level 4 endpoints, status code choices Running API with 422 and 409 cases
7-8 Level 5 error handler and response contract Written error format document
9-10 Level 6 nested orders and strict mode Order endpoint with nested errors
11-12 Combine into a small mini-project (student registry API) Git repository with README
13 Add pytest tests for every rejection rule Passing test suite
14 Clean up, document, self-score against the table Final write-up

CSJMU students: where Pydantic practice fits your course context

CSJMU in Kanpur, formerly Kanpur University, describes itself on its Academic Calendar page as holding an A++ NAAC rating. The same page lists separate calendars for the University Campus and for Affiliated Colleges, including a 2026-27 Odd Semester calendar for the campus. Since your timetable depends on which calendar applies to you, check it before scheduling a 14-day block.

For computing students, the university hosts a proposed BCA syllabus PDF with semester-coded papers such as BCA-S101T Computer Fundamental & Office Automation. That is an excerpt-level view and may be outdated, so read the current version for your batch. Python API work is self-directed practice that complements whatever your syllabus covers; do not assume it maps to a specific paper.

On internships, the B.Com. (Honors) ordinance (a 2019 document by file name) mentions six to eight weeks of summer internship in a business or industrial unit. It gives no credits, evaluation scheme or mention of online internships. Rules for other programmes were not verified, so a two-week online internship should be treated as extra learning unless your department confirms otherwise. It does not carry stated academic credit.

The same ordinance says programmes should have at least one computer lab and campus Wi-Fi, but do not rely on that for practice time; a personal laptop setup is more dependable.

Verification steps for CSJMU students before you count this work

  1. Identify whether you study on the University Campus or at an affiliated college, then open the matching calendar on the Academic Calendar page.
  2. Download the latest syllabus PDF for your exact programme and batch, and check whether it names a project, training or internship component.
  3. Email your department coordinator asking in writing: “Is an online internship accepted for the training or project requirement, and what documents are required?”
  4. Ask whether the rules are set by the university or by your college, and request the circular or ordinance number.
  5. If you want to cite the internship in a project file, ask your faculty guide whether a completion certificate is accepted as supporting evidence.

Self-review checklist for validation quality

  • Every required field is truly required, and every optional field has a deliberate default.
  • Numbers have both lower and upper bounds where sensible.
  • Strings have maximum lengths, so one request cannot send megabytes into a name field.
  • Enumerated values use Literal or Enum.
  • Cross-field rules sit in a model validator.
  • Business-state conflicts return 409, not 422.
  • Error responses use one consistent shape with stable machine-readable codes.
  • Error messages never reveal secrets, stack traces or passwords.
  • You have at least one test per rejection rule.

Status codes for bad requests: which one to return

Situation Status Reason
Body is not valid JSON 400 The request cannot be parsed at all
JSON is valid but fields fail rules 422 Syntax fine, content invalid (FastAPI default)
Duplicate unique value 409 Conflicts with current server state
Missing or bad credentials 401 Caller is not authenticated
Authenticated but not allowed 403 Permission problem, not a data problem
Record ID does not exist 404 Resource not found

Frequently Asked Questions

Should I validate in Pydantic or in my route function?

Put shape, type, range and format rules in the Pydantic model, and put rules that need a database or another service in route or service code. That split keeps models reusable and easy to test without any infrastructure.

Why does Pydantic accept the string “25” for an integer field?

By default Pydantic runs in lax mode and converts safely convertible values. If you want a string rejected, use strict mode on that field or on the whole model.

What is the difference between a 400 and a 422 response?

400 usually signals a malformed request that could not be parsed, while 422 signals a parsed request whose values break your rules. FastAPI uses 422 for model validation failures.

Can I return all errors at once instead of the first one?

Yes. Pydantic collects every failing field into a single ValidationError, and errors() returns them as a list. Raising your own exception inside route code stops at the first problem, so keep field rules inside the model.

How do I validate an email properly?

Install the optional extra with pip install "pydantic[email]" and use EmailStr. A regular expression alone is a weak check, and the only true confirmation is sending a verification email.

Is Pydantic validation enough to stop injection attacks?

No. It limits what shapes of data get in, but you still need parameterised database queries and output escaping. Treat validation as one layer, not the whole defence.

Will completing these exercises count for credit at CSJMU?

Nothing verified states that it will, and the internship is not credit-bearing. Ask your department in writing whether any certificate is accepted for project or training requirements.

Do I need FastAPI to use Pydantic?

No. Pydantic works in any Python code, including scripts, Flask apps and data pipelines. FastAPI simply connects it to HTTP requests and responses automatically.

Related Python & Backend Engineering guides

Ready to put this into practice?

Join the free two-week CareerSwami online internship in Python & Backend Engineering. The internship, the offer letter and the completion certificate are free.

Start your free 2-week internship

Web Stories

Scroll to Top