Skip to content

WIP: 29177 - ForeignKeys on Unmanaged models get those serialized into migrations#9741

Closed
kezabelle wants to merge 1 commit intodjango:masterfrom
kezabelle:tickets/29177_unmanaged_models_with_fks
Closed

WIP: 29177 - ForeignKeys on Unmanaged models get those serialized into migrations#9741
kezabelle wants to merge 1 commit intodjango:masterfrom
kezabelle:tickets/29177_unmanaged_models_with_fks

Conversation

@kezabelle
Copy link
Copy Markdown
Contributor

Trac ticket: https://code.djangoproject.com/ticket/29177

Marking as work in progress because it's just a sketch right now, and will need rebasing & tidying up. I've left comments saying as much in the code, so those at least will need to go :)

Specific questions I have:

  • how do I verify that the addition of the FK into the migration state won't try to create constraints/indexes? I suspect that's maybe what the AddField on test_unmanaged_model_fk_to_managed_model might try and do?
  • is it correct that an FK from an unmanaged model to a managed model leads to a different series of operations (CreateModel, CreateModel, AddField) than a FK from an unmanaged model to another unmanaged model (CreateModel, CreateModel)?
  • I feel like adding an FK to an existing unmanaged model ought to do something (test_adding_an_fk_to_an_unmanaged_model) so that the serialized field is available in subsequent migrations, even if it's a no-op. But it doesn't seem to, given the way I've used self.get_changes ... so maybe I've used it wrong?

@MarkusH
Copy link
Copy Markdown
Member

MarkusH commented Mar 2, 2018

  • how do I verify that the addition of the FK into the migration state won't try to create constraints/indexes? I suspect that's maybe what the AddField on test_unmanaged_model_fk_to_managed_model might try and do?

I'd rely on the fact that AddField checks if it is allowed to do any database operations at the beginning of its database_forward method.

  • is it correct that an FK from an unmanaged model to a managed model leads to a differentseries of operations (CreateModel, CreateModel, AddField) than a FK from an unmanaged model to another unmanaged model (CreateModel, CreateModel)?

This is more likely due to the lexicographical ordering of models and the followup optimization of operations. Try swapping the model names and you should see the exact opposite behavior.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/_managed/_unmanaged/ I think.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a similar test case where the FK points to a managed model, please :)

@kezabelle kezabelle force-pushed the tickets/29177_unmanaged_models_with_fks branch from 8576f37 to 2423688 Compare March 2, 2018 14:41
@kezabelle
Copy link
Copy Markdown
Contributor Author

Did some tidy-up and added in the extra test test_adding_an_fk_to_an_unmanaged_model_where_fk_is_managed ... both of the tests which exercise adding to existing migrations will fail though, because no changes are detected, but I still feel like one ought to be?

@MarkusH
Copy link
Copy Markdown
Member

MarkusH commented Mar 2, 2018

I think I found the explanation for that as well.

  • generate_added_fields iterates over new_field_keys - old_field_keys:
    for app_label, model_name, field_name in sorted(self.new_field_keys - self.old_field_keys):
  • new_field_keys is derived from kept_model_keys:
    self.new_field_keys = {
    (app_label, model_name, x)
    for app_label, model_name in self.kept_model_keys
    for x, y in self.to_state.models[app_label, model_name].fields
    }
  • kept_model_keys is old_model_keys & new_model_keys:
    self.kept_model_keys = self.old_model_keys & self.new_model_keys
  • old_model_keys and new_model_keys only contain managed, non-proxy models:
    for al, mn in self.from_state.models:
    model = self.old_apps.get_model(al, mn)
    if not model._meta.managed:
    self.old_unmanaged_keys.add((al, mn))
    elif al not in self.from_state.real_apps:
    if model._meta.proxy:
    self.old_proxy_keys.add((al, mn))
    else:
    self.old_model_keys.add((al, mn))
    for al, mn in self.to_state.models:
    model = self.new_apps.get_model(al, mn)
    if not model._meta.managed:
    self.new_unmanaged_keys.add((al, mn))
    elif (
    al not in self.from_state.real_apps or
    (convert_apps and al in convert_apps)
    ):
    if model._meta.proxy:
    self.new_proxy_keys.add((al, mn))
    else:
    self.new_model_keys.add((al, mn))

So, this is going to be considerably more work :( Though, at the moment, I'm struggling to understand why we need to keep the distinction between concrete, proxy and unmanaged models in the autodetector 🤔

@kezabelle
Copy link
Copy Markdown
Contributor Author

kezabelle commented Mar 5, 2018

Thanks for investigating further for me. As a bit of exploration, I've tried removing the code:

if not model._meta.managed: 
    self.old_unmanaged_keys.add((al, mn))

and

if not model._meta.managed: 
    self.new_unmanaged_keys.add((al, mn))

from that part of the autodetector, and that does fix the lack of operations for the adding of a FK to existing unmanaged models. It also turns the CreateModel, CreateModel, AddField into just a CreateModel, CreateModel pair. And doesn't cause any tests to error when running ./runtests.py (that is, using sqlite3 locally).

I don't know that just excising the code would be the correct fix -- as you say, there's a whole built-in distinction for unmanaged models, and it's a bigger ticket than either of us thought -- so I've not pushed the change, but it's interesting.

@felixxm
Copy link
Copy Markdown
Member

felixxm commented Jan 9, 2020

Closing due to inactivity.

@felixxm felixxm closed this Jan 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants