Clean up and simplify the common primary key constraint management.

Review Request #11097 — Created July 26, 2020 and submitted — Latest diff uploaded

Information

Django Evolution
master

Reviewers

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 a ManyToManyField. By default, it
would add ADD CONSTRAINT and DROP CONSTRAINT statements surrounding
these renames, and to opt out of it, a subclass (SQLite's, for instance)
would have to set support_constraints = False.

There were some issues with the implementation:

  1. 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 new Model.Meta.constraints support.

  2. 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.

  3. The code used to stash away existing references, drop constraints,
    and then re-add them was largely duplicated across rename_table()
    and add_primary_key_field_constraints/remove_field_constraints
    (with minor variations in when it considers a field something worth
    tracking).

  4. 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.

  5. 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() and restore_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.

Commits

Files

    Loading...