Clean up and simplify the common primary key constraint management.
Review Request #11097 — Created July 26, 2020 and submitted
The base evolver backend code provided some default functionality for
updating constraints for a field with a primary key when renaming a
model or renaming the table behind aManyToManyField
. By default, it
would addADD CONSTRAINT
andDROP CONSTRAINT
statements surrounding
these renames, and to opt out of it, a subclass (SQLite's, for instance)
would have to setsupport_constraints = False
.There were some issues with the implementation:
That flag was unclear. It implies SQLite doesn't support constraints,
which is not true. It does, the backend just doesn't want these
statements to be generated (sine they must be managed an entirely
different way). It was also doubly unclear in the context of
Django 2.2's newModel.Meta.constraints
support.The flag was also hacky. It was really there to turn off behavior
SQLite didn't want, rather than SQLite just implementing its own
behavior like it does for other operations.The code used to stash away existing references, drop constraints,
and then re-add them was largely duplicated acrossrename_table()
andadd_primary_key_field_constraints
/remove_field_constraints
(with minor variations in when it considers a field something worth
tracking).The naming of those two functions weren't at all consistent, and the
former didn't leave the door open for other forms of automatic field
constraint management.It imposed some code flow choices on table renaming that made code a
bit harder to really understand than was necessary.This change reworks all this, in preparation for the new Django 2.2
constraint support.The
support_constraints
flag is completely gone now. Instead, the
rename_table()
code that used it just hard-codes usage of constraints,
and SQLite completely overrides it to perform table renaming its own
way. That simplifies a lot of the code and capability checking.Instead of having the code duplicated in places, there's now central
stash_field_ref_constraints()
andrestore_field_ref_constraints()
functions that are meant to work together.
stash_field_ref_constraints()
takes in the operations that are to be
performed (mapping of replaced table names or replaced fields),
calculates a bunch of state it needs, and determines the correct
references to track and the SQL to generate. It's able to be used for
both table and column renaming, and down the road can be updated for
additional management.This function returns the SQL and a
stash
object, which is just a
dictionary of state that can be passed in to
restore_field_ref_constraints()
. That function then updates
model/field state and generates SQL based on that stashed state.There's still plenty of room for improvement, but this sets us up to be
able to pursue that without the hackiness we had before.
Unit tests pass for all database types on all supported versions of
Django, on Python 2.7 and 3.x.