In my djangoapp, the initial migration creates a model called UserProfile with a field called lowest_priority_to_alert. There is then later a migration that loads in some fixture data using a custom RunPython migration so that it can be used in our e2e tests. Finally there is a migration that removes the lowest_priority_to_alert field.
When I run these migrations, I can see that the initial migration is run, then the migration to load in the fixture data. However, I get the following error: AttributeError: 'UserProfile' object has no attribute 'lowest_priority_to_alert'
I will show my migration bellow but I have added some logging, the first to see which fields the UserProfile model currently has and the second to see what the fixture data is. All this looks normal:
['id', 'deleted_at', 'account_type', 'job_title', 'timezone', 'locale', 'api_only', 'api_burst_rate', 'api_sustained_rate', 'saved_frontend_settings_v2', 'lowest_priority_to_alert', 'mobile_push_notifications', 'dark_hour_start', 'dark_hour_end', 'is_fake', 'is_hidden', 'mobile_tabbar_options', 'menu_options', 'fixed_frontend_settings', 'disable_screen_recording', 'business', 'group', 'user', 'clock_format', 'csv_delimiter', 'temperature_unit', 'is_superstaff_user', 'set_of_cards', 'webapp_cards_v3', 'feature_flags']
[
{
"model": "collector.userprofile",
"pk": 1,
"fields": {
"deleted_at": null,
"user": 1,
"business": 795,
"group": null,
"account_type": 1,
"job_title": null,
"timezone": "Europe/London",
"locale": "en-GB",
"api_only": false,
"api_burst_rate": 200,
"api_sustained_rate": 10000,
"saved_frontend_settings_v2": {
"sales": {
"tax": 0,
"comps": 0,
"promos": 0,
"service": 0
},
"labour": {
"showSalaried": 1,
"labourOverhead": 0,
"spreadSalaried": 0
}
},
"lowest_priority_to_alert": 3000,
"mobile_push_notifications": true,
"dark_hour_start": null,
"dark_hour_end": null,
"is_superstaff_user": false,
"is_fake": false,
"is_hidden": false,
"mobile_tabbar_options": [
"home",
"sales",
"social",
"employee",
"more"
],
"menu_options": [],
"fixed_frontend_settings": null,
"disable_screen_recording": false,
"temperature_unit": null,
"clock_format": null,
"csv_delimiter": null,
"webapp_cards_v3": [],
"set_of_cards": [],
"feature_flags": []
}
}
]
I am very confused as to what is happening. I have triple checked that my migrations are running in the correct order. Here is my migration:
from pathlib import Path
from django.conf import settings
from django.core import serializers
from django.db import connections, migrations
from common.sharding.resharding.table_relationships import get_fk_models
def create_central_test_data(folder_path: Path):
def forwards_code(apps, schema_editor):
if not settings.TESTING:
return
db_alias = schema_editor.connection.alias
if db_alias != "default":
return
models = get_fk_models(get_central_models=True, get_sharded_models=False)
for model in reversed(models):
if not folder_path.joinpath(f"{model._meta.db_table}.json").is_file():
continue
model = apps.get_model(model._meta.label)
print("deleting from", model)
if hasattr(model, "all_objects"):
model.all_objects.using(db_alias).hard_delete()
elif hasattr(model, "objects"):
model.objects.using(db_alias).delete()
else:
model.protected_objects.using(db_alias).delete()
for model in models:
if not folder_path.joinpath(f"{model._meta.db_table}.json").is_file():
continue
model = apps.get_model(model._meta.label)
print("writing to", model)
# drop trigger so we can insert IDs with the same value as stored
drop_trigger_sql = f"DROP TRIGGER IF EXISTS public_{model._meta.db_table}_insert ON {model._meta.db_table};"
with connections["default"].cursor() as cursor:
cursor.execute(drop_trigger_sql)
with open(folder_path.joinpath(f"{model._meta.db_table}.json"), "r") as f:
serialized = f.read()
deserialized = serializers.deserialize(
"json", serialized, ignorenonexistent=True
)
if model._meta.db_table == "collector_userprofile":
print(
f"There current {model._meta.db_table} count is {model.objects.count()}"
)
print([f.name for f in model._meta.get_fields()])
print(serialized)
if hasattr(model, "objects"):
model.objects.using(db_alias).bulk_create(
d.object for d in deserialized
)
else:
model.protected_objects.using(db_alias).bulk_create(
d.object for d in deserialized
)
set_id_sequence_sql = f"""
SELECT setval(
'public.{model._meta.db_table}_id_seq',
COALESCE((SELECT MAX(id)+1 FROM public.{model._meta.db_table}), 1),
false
);
"""
with connections["default"].cursor() as cursor:
cursor.execute(set_id_sequence_sql)
return {"code": forwards_code, "reverse_code": migrations.RunPython.noop}