A database makes relationships explicit
Folders are good at storing files. They are less suited to representing that several events come from one document, one proposition has two conflicting sources, or a draft depends on a particular version of a fact.
A relational database stores tables and relationships. SQLite can run within a Python program without a separate database server. This is useful for learning; a multi-user production design requires additional decisions about access, concurrency, operations, and backups. [source]
Run a self-contained example
With Python 3 installed, save this as matter_lab.py. Run it with your Python command. It creates a temporary database in memory, so it does not alter any persistent file.
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("PRAGMA foreign_keys = ON")
con.executescript("""
CREATE TABLE documents (
id TEXT PRIMARY KEY,
title TEXT NOT NULL
);
CREATE TABLE events (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL REFERENCES documents(id),
event_date TEXT,
description TEXT NOT NULL,
source_location TEXT NOT NULL
);
""")
con.execute("INSERT INTO documents VALUES (?, ?)",
("A01", "Fictional schedule proposal"))
con.execute("INSERT INTO events VALUES (?, ?, ?, ?, ?)",
("E01", "A01", "2026-03-20", "Inspection proposed", "paragraph 2"))
for row in con.execute("""
SELECT e.id, e.event_date, e.description, d.title, e.source_location
FROM events AS e JOIN documents AS d ON e.document_id = d.id
ORDER BY e.event_date, e.id
"""):
print(row)
con.close()The placeholders pass values separately from SQL instructions. JOIN combines related rows. The foreign key requires an event's document ID to exist in the documents table when enforcement is enabled. [source] [source]
Understand what the database enforces
It can reject an event pointing to a nonexistent document ID. It cannot know whether “paragraph 2” actually supports “Inspection proposed.” A valid relationship is not necessarily a true relationship.
The example also stores the proposed inspection date as an event date. A fuller schema should preserve status, date basis, original date wording, source passage, reviewer, and version. Add those because the questions require them, not because more columns automatically create a better system.
Try it: break a relationship
In a copy of the script, change the event's document ID from A01 to A99 while leaving the document insert unchanged. Predict the result, then run it.
Check your understanding
With foreign-key enforcement enabled, the insert should fail because A99 is absent. Restore A01. Next change the description to “Inspection completed.” The database will accept it because that is a substantive error outside the current constraints. The contrast explains why data validation and evidence verification must work together.
Extend one question at a time
Add a field for review status and query only reviewed events. Add contrary-source relationships when you need them. Keep a data dictionary defining each field. Before persistence or shared access, design backup, access control, migration, and correction procedures. The introductory script demonstrates a relationship; it does not constitute a complete matter-management product.