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.
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.
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.
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"),
)
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.
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.