Skip to content
Aabha AI Academy

Module 3 of 9 · Lesson 6 of 26

Model relationships and enforce database constraints

Work through model relationships and enforce database constraints using a runnable reference, a focused regression check and a local extension.

Read in any order. All lessons stay open, including after an unanswered or incorrect check.

In this lesson you will model relationships and enforce database constraints. Work with the Equipment Rental API in the downloadable lab. The reference is a complete solution with separate lesson checks, so you can inspect the answer, make a deliberate local change and verify its behavior.
A rental belongs to one equipment item and one user. Foreign keys express those relationships in the database, so a row cannot reference an owner that does not exist. The Equipment.rentals relationship lets Python navigate the association; it does not replace the foreign key. A unique equipment name and check constraints for positive quantities protect writes from any client, including scripts that bypass the API model.

Choose constraints from a concrete invariant. A quantity of zero cannot reserve equipment, and a daily rate cannot be negative. The database rejects either even if application validation is accidentally skipped. Status has a closed set of values, rather than accepting arbitrary strings. These row constraints cannot enforce total active rentals against equipment capacity by themselves: that rule spans rows and needs a transaction protocol in lesson 10. The relationships use lazy='raise' so accidental attribute access cannot quietly issue more queries. Explicit loading is introduced later. Do not solve referential errors by disabling constraints; fix the ordering or validity of the write.
Worked source: equipment/models.py, Rental.

Compare User, Equipment and Rental in models.py. The lesson06 check attempts a rental for a nonexistent UUID owner and separately inserts equipment with zero capacity. Both writes must fail at the database boundary and roll back. Check that no row from either failed transaction remains.
pythonCopyable
class Rental(Base):
    __tablename__ = "rentals"
    id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4)
    equipment_id: Mapped[int] = mapped_column(ForeignKey("equipment.id"))
    owner_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"))
    quantity: Mapped[int]
    status: Mapped[str] = mapped_column(String(20), default="active")
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    equipment: Mapped[Equipment] = relationship(back_populates="rentals", lazy="raise")
    __table_args__ = (
        CheckConstraint("quantity >= 1", name="positive_rental_quantity"),
        CheckConstraint("status IN ('active','checked_in')", name="rental_status"),
    )
TerminalPython 3.13 virtual environment; Docker running; extracted lab directory
python run_checks.py -k lesson06

Expected result The selected lesson test passes against a new temporary PostgreSQL database; the container is removed afterward.

Keep for reference

Equipment Rental lab and lesson checks

ZIP containing Python source, real Alembic migrations, 26 lesson checks, a dependency lock and text instructions. Extract it before following the local exercise.

Download Equipment Rental lab and lesson checks

Practise locally

Add a test for a duplicate equipment name and another for an invalid rental status. Identify whether each failure is a unique constraint or check constraint. Draw the two rental foreign keys and describe deletion behavior; the reference does not silently cascade deletion of users or equipment into rental history.

The lesson check verifies the reference behavior. Add your own assertions for your change. Local practice is not uploaded or scored by this learning release.

Pause and reflect

What failure does this lesson prevent, and which assertion in lesson06 would expose it?

Use a concrete input, expected result and limitation from your local work. Saving a reflection does not certify the project.

Optional knowledge check

Why retain a foreign key when the API already validates an owner?

Enforce ownership only with a foreign key.

Try another answer. A valid relationship does not decide whether this caller may access the record.

It protects the relationship for every database writer.

Correct. The invariant survives alternate clients and accidental application omissions.

It makes owner validation unnecessary in all client responses.

Try another answer. Database integrity does not replace clear input and error contracts at the HTTP boundary.

This practice does not assess your project or award a certificate.

Your reading progress

Progress is saved in this browser when storage is available.

Sign in to save across devices · Create an optional account