ForeignKey is now a frozen dataclass, restoring hashability

The namedtuple-to-dataclass change made ForeignKey unhashable, breaking
set(table.foreign_keys) and dict-key usage that worked in 3.x. frozen=True
restores immutability and hashability. Equality and hashing cover all
compared fields including on_delete/on_update - two foreign keys differing
only in their actions are different constraints.

Refs https://github.com/simonw/sqlite-utils/issues/769#issuecomment-4900034150

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Simon Willison 2026-07-06 21:35:45 -07:00
commit 404e935b63
4 changed files with 51 additions and 8 deletions

View file

@ -578,3 +578,35 @@ def test_add_compound_foreign_key_guesses_pk_declaration_order(fresh_db):
fresh_db["child"].insert({"id": 1, "x": "A", "y": "B"}, pk="id")
fresh_db["child"].add_foreign_key(("x", "y"), "other")
assert fresh_db["child"].foreign_keys[0].other_columns == ("a", "b")
def test_foreign_keys_are_hashable(fresh_db):
# set() over foreign_keys worked with the 3.x namedtuple and must
# keep working with the dataclass
fresh_db["p"].insert({"id": 1}, pk="id")
fresh_db["c"].insert(
{"id": 1, "pid": 1}, pk="id", foreign_keys=[("pid", "p", "id")]
)
fks = set(fresh_db["c"].foreign_keys)
assert len(fks) == 1
assert ForeignKey("c", "pid", "p", "id") in fks
# Usable as dict keys too
assert {fk: True for fk in fks}
def test_foreign_key_is_immutable():
import dataclasses
fk = ForeignKey("c", "pid", "p", "id")
with pytest.raises(dataclasses.FrozenInstanceError):
fk.table = "other"
def test_foreign_key_equality_and_hash_include_actions():
# Two foreign keys differing only in ON DELETE behavior are different
# constraints - they compare unequal and hash separately
plain = ForeignKey("c", "pid", "p", "id")
cascade = ForeignKey("c", "pid", "p", "id", on_delete="CASCADE")
assert plain != cascade
assert len({plain, cascade}) == 2
assert plain == ForeignKey("c", "pid", "p", "id")