def create_default_setup_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") AuthenticatorStaticStage = apps.get_model( "authentik_stages_authenticator_static", "AuthenticatorStaticStage") db_alias = schema_editor.connection.alias flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-authenticator-static-setup", designation=FlowDesignation.STAGE_CONFIGURATION, defaults={ "name": "default-authenticator-static-setup", "title": "Setup Static OTP Tokens", }, ) stage, _ = AuthenticatorStaticStage.objects.using( db_alias).update_or_create(name="default-authenticator-static-setup", defaults={"token_count": 6}) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=stage, defaults={"order": 0}) for stage in AuthenticatorStaticStage.objects.using(db_alias).filter( configure_flow=None): stage.configure_flow = flow stage.save()
def seed(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Song = apps.get_model('playlister', 'Song') Playlist = apps.get_model('playlister', 'Playlist') PlaylistEntry = apps.get_model('playlister', 'PlaylistEntry') db_songs = [] for i in songs: s = Song() s.artist = i.get('artist', None) s.title = i.get('title', None) s.album = 'Uknown' s.save() db_songs.append(s) for i in range(10): pl = Playlist() pl.name = f'Seeded playlist {i}' pl.source = 'USER' pl.save() for j in range(10): pe = PlaylistEntry() pe.order = j pe.song = choice(db_songs) pe.playlist = pl pe.save()
def notify_update(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias Group = apps.get_model("authentik_core", "Group") PolicyBinding = apps.get_model("authentik_policies", "PolicyBinding") EventMatcherPolicy = apps.get_model("authentik_policies_event_matcher", "EventMatcherPolicy") NotificationRule = apps.get_model("authentik_events", "NotificationRule") NotificationTransport = apps.get_model("authentik_events", "NotificationTransport") admin_group = (Group.objects.using(db_alias).filter( name="authentik Admins", is_superuser=True).first()) policy, _ = EventMatcherPolicy.objects.using(db_alias).update_or_create( name="default-match-update", defaults={"action": EventAction.UPDATE_AVAILABLE}, ) trigger, _ = NotificationRule.objects.using(db_alias).update_or_create( name="default-notify-update", defaults={ "group": admin_group, "severity": NotificationSeverity.ALERT }, ) trigger.transports.set( NotificationTransport.objects.using(db_alias).filter( name="default-email-transport")) trigger.save() PolicyBinding.objects.using(db_alias).update_or_create( target=trigger, policy=policy, defaults={ "order": 0, }, )
def create_default_validate_stage(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") AuthenticatorValidateStage = apps.get_model( "authentik_stages_authenticator_validate", "AuthenticatorValidateStage") db_alias = schema_editor.connection.alias auth_flows = Flow.objects.using(db_alias).filter( slug="default-authentication-flow") if not auth_flows.exists(): return # If there's already a validation stage in the flow, skip if (AuthenticatorValidateStage.objects.using(db_alias).filter( flow__slug="default-authentication-flow").exists()): return validate_stage, _ = AuthenticatorValidateStage.objects.using( db_alias).update_or_create( name="default-authentication-mfa-validation", defaults={ "device_classes": default_device_classes, }, ) FlowStageBinding.objects.using(db_alias).update_or_create( target=auth_flows.first(), stage=validate_stage, defaults={ "order": 30, }, )
def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias from django.contrib.sessions.backends.cache import KEY_PREFIX, SessionStore from django.core.cache import cache AuthenticatedSession = apps.get_model("authentik_core", "AuthenticatedSession") User = apps.get_model("authentik_core", "user") session_keys = cache.keys(KEY_PREFIX + "*") for key in session_keys: key = key.replace(KEY_PREFIX, "") store = SessionStore(key) data = store.load() if data == {} or "_auth_user_id" not in data: continue if ( AuthenticatedSession.objects.using(db_alias) .filter(session_key=key) .exists() ): continue users = User.objects.using(db_alias).filter(pk=data.get("_auth_user_id")) if not users.exists(): continue AuthenticatedSession.objects.using(db_alias).create( session_key=key, user=users.first(), expires=data.get("_session_expiry", now()), )
def create_default_invalidation_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") UserLogoutStage = apps.get_model("authentik_stages_user_logout", "UserLogoutStage") db_alias = schema_editor.connection.alias UserLogoutStage.objects.using(db_alias).update_or_create( name="default-invalidation-logout") flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-invalidation-flow", designation=FlowDesignation.INVALIDATION, defaults={ "name": "Logout", }, ) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=UserLogoutStage.objects.using(db_alias).first(), defaults={ "order": 0, }, )
def create_default_provider_authorization_flow( apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") ConsentStage = apps.get_model("authentik_stages_consent", "ConsentStage") db_alias = schema_editor.connection.alias # Empty flow for providers where consent is implicitly given Flow.objects.using(db_alias).update_or_create( slug="default-provider-authorization-implicit-consent", designation=FlowDesignation.AUTHORIZATION, defaults={"name": "Authorize Application"}, ) # Flow with consent form to obtain explicit user consent flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-provider-authorization-explicit-consent", designation=FlowDesignation.AUTHORIZATION, defaults={"name": "Authorize Application"}, ) stage, _ = ConsentStage.objects.using(db_alias).update_or_create( name="default-provider-authorization-consent") FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=stage, defaults={"order": 0})
def create_default_authentication_flow( apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") PasswordStage = apps.get_model("authentik_stages_password", "PasswordStage") UserLoginStage = apps.get_model("authentik_stages_user_login", "UserLoginStage") IdentificationStage = apps.get_model("authentik_stages_identification", "IdentificationStage") db_alias = schema_editor.connection.alias identification_stage, _ = IdentificationStage.objects.using( db_alias).update_or_create( name="default-authentication-identification", defaults={ "user_fields": [UserFields.E_MAIL, UserFields.USERNAME], }, ) password_stage, _ = PasswordStage.objects.using(db_alias).update_or_create( name="default-authentication-password", defaults={ "backends": [BACKEND_INBUILT, BACKEND_LDAP, BACKEND_APP_PASSWORD] }, ) login_stage, _ = UserLoginStage.objects.using(db_alias).update_or_create( name="default-authentication-login") flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-authentication-flow", designation=FlowDesignation.AUTHENTICATION, defaults={ "name": "Welcome to authentik!", }, ) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=identification_stage, defaults={ "order": 10, }, ) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=password_stage, defaults={ "order": 20, }, ) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=login_stage, defaults={ "order": 100, }, )
def assign_sources(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias IdentificationStage = apps.get_model("authentik_stages_identification", "identificationstage") Source = apps.get_model("authentik_core", "source") sources = Source.objects.all() for stage in IdentificationStage.objects.all().using(db_alias): stage.sources.set(sources) stage.save()
def remove_pb_prefix_users(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): alias = schema_editor.connection.alias User = apps.get_model("authentik_core", "User") Outpost = apps.get_model("authentik_outposts", "Outpost") for outpost in Outpost.objects.using(alias).all(): matching = User.objects.using(alias).filter( username=f"pb-outpost-{outpost.uuid.hex}") if matching.exists(): matching.delete()
def migrate_nominations(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: Nomination = apps.get_model("api", "Nomination") NominationEntry = apps.get_model("api", "NominationEntry") for nomination in Nomination.objects.all(): nomination_entry = NominationEntry(nomination=nomination, actor=nomination.actor, reason=nomination.reason, inserted_at=nomination.inserted_at) nomination_entry.save()
def create_default_password_change(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") PromptStage = apps.get_model("authentik_stages_prompt", "PromptStage") Prompt = apps.get_model("authentik_stages_prompt", "Prompt") UserWriteStage = apps.get_model("authentik_stages_user_write", "UserWriteStage") db_alias = schema_editor.connection.alias flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-password-change", designation=FlowDesignation.STAGE_CONFIGURATION, defaults={"name": "Change Password"}, ) prompt_stage, _ = PromptStage.objects.using(db_alias).update_or_create( name="Change your password", ) password_prompt, _ = Prompt.objects.using(db_alias).update_or_create( field_key="password", defaults={ "label": "Password", "type": FieldTypes.PASSWORD, "required": True, "placeholder": "Password", "order": 300, }, ) password_rep_prompt, _ = Prompt.objects.using(db_alias).update_or_create( field_key="password_repeat", defaults={ "label": "Password (repeat)", "type": FieldTypes.PASSWORD, "required": True, "placeholder": "Password (repeat)", "order": 301, }, ) prompt_stage.fields.add(password_prompt) prompt_stage.fields.add(password_rep_prompt) prompt_stage.save() user_write, _ = UserWriteStage.objects.using(db_alias).update_or_create( name="default-password-change-write") FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=prompt_stage, defaults={"order": 0}) FlowStageBinding.objects.using(db_alias).update_or_create( target=flow, stage=user_write, defaults={"order": 1})
def code(apps: Apps, unused_schema_editor=None): """Create the mail templates for all existing events""" mailtemplate_class = apps.get_model("invite", "MailTemplate") event_class = apps.get_model("invite", "Event") subject, text, html = _get_mail_templates() for event in event_class.objects.all(): mailtemplate_class.objects.create( event=event, subject=subject, text=text, html=html, )
def unmigrate_nominations(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: Nomination = apps.get_model("api", "Nomination") NominationEntry = apps.get_model("api", "NominationEntry") for entry in NominationEntry.objects.all(): nomination = Nomination.objects.get(pk=entry.nomination.id) nomination.actor = entry.actor nomination.reason = entry.reason nomination.inserted_at = entry.inserted_at nomination.save()
def set_default_group_mappings(apps: Apps, schema_editor): LDAPPropertyMapping = apps.get_model("authentik_sources_ldap", "LDAPPropertyMapping") LDAPSource = apps.get_model("authentik_sources_ldap", "LDAPSource") db_alias = schema_editor.connection.alias for source in LDAPSource.objects.using(db_alias).all(): if source.property_mappings_group.exists(): continue source.property_mappings_group.set( LDAPPropertyMapping.objects.using(db_alias).filter( managed="goauthentik.io/sources/ldap/default-name")) source.save()
def create_profiles(apps: Apps, schema_editor): """ Create profiles for all the non-discord users """ Profile = apps.get_model("account", "Profile") User = apps.get_model(settings.AUTH_USER_MODEL) for user in User.objects.all(): try: user.profile except Profile.DoesNotExist: profile = Profile( preferred_username=user.username.replace("#", "."), user=user ) profile.save()
def create_default_admin_group(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias Group = apps.get_model("authentik_core", "Group") User = apps.get_model("authentik_core", "User") # Creates a default admin group group, _ = Group.objects.using(db_alias).get_or_create( is_superuser=True, defaults={ "name": "authentik Admins", }, ) group.users.set(User.objects.filter(username="******")) group.save()
def update_default_source_enrollment_flow_binding( apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") FlowStageBinding = apps.get_model("authentik_flows", "FlowStageBinding") db_alias = schema_editor.connection.alias flows = Flow.objects.using(db_alias).filter( slug="default-source-enrollment") if not flows.exists(): return flow = flows.first() binding = FlowStageBinding.objects.get(target=flow, order=0) binding.re_evaluate_policies = True binding.save()
def revert_create_group_permission(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): """ Revert what's done in create_group_permission """ TheGroup: Group = apps.get_model('auth', 'Group') ThePermission: Permission = apps.get_model('auth', 'Permission') TheGroup.objects.filter( name__in=['approved_supervisors', 'course_convener', 'mgt_superusers' ]).delete() ThePermission.objects.filter( codename__in=['can_convene', 'can_supervise', 'is_mgt_superuser' ]).delete()
def setUpBeforeMigration(self, apps: Apps): Repository = apps.get_model('gitmate_config', 'Repository') Installation = apps.get_model('gitmate_config', 'Installation') self.inst = Installation(provider='github', identifier=81451) self.inst.save() self.repo = Repository(provider='github', installation=self.inst, full_name=environ['GITHUB_TEST_REPO']) self.repo.save() Repository(provider='coala.io', installation=self.inst, full_name='some/fancy/name').save()
def migrate_infractions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: Infraction = apps.get_model("api", "Infraction") for infraction in Infraction.objects.filter(type="voice_ban"): infraction.type = "voice_mute" infraction.save()
def update_name_temp(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): OAuth2Provider = apps.get_model("authentik_providers_oauth2", "OAuth2Provider") db_alias = schema_editor.connection.alias for provider in OAuth2Provider.objects.using(db_alias).all(): provider.name_temp = provider.name provider.save()
def revert_create_assessment_templates( apps: Apps, schema_editor: BaseDatabaseSchemaEditor): TheAssessmentTemplate: AssessmentTemplate = apps.get_model( 'research_mgt', 'AssessmentTemplate') TheAssessmentTemplate.objects.filter( name__in=['report', 'artifact', 'presentation', 'custom']).delete()
def set_default_token_key(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias Token = apps.get_model("authentik_core", "Token") for token in Token.objects.using(db_alias).all(): token.key = token.pk.hex token.save()
def set_managed_flag(apps: Apps, schema_editor): ScopeMapping = apps.get_model("authentik_providers_oauth2", "ScopeMapping") db_alias = schema_editor.connection.alias for mapping in ScopeMapping.objects.using(db_alias).filter( name__startswith="Autogenerated "): mapping.managed = scope_uid_map[mapping.scope_name] mapping.save()
def revert_create_courses(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): TheCourse: Course = apps.get_model('research_mgt', 'Course') TheCourse.objects.filter(course_number__in=[ 'COMP2710', 'COMP3710', 'COMP3740', 'COMP3770', 'COMP4560', 'COMP6470', 'COMP8755' ]).delete()
def create_default_pre_authentication_flow(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): Flow = apps.get_model("authentik_flows", "Flow") SAMLSource = apps.get_model("authentik_sources_saml", "samlsource") db_alias = schema_editor.connection.alias # Empty flow for providers where consent is implicitly given flow, _ = Flow.objects.using(db_alias).update_or_create( slug="default-source-pre-authentication", designation=FlowDesignation.STAGE_CONFIGURATION, defaults={"name": "Pre-Authentication", "title": ""}, ) for source in SAMLSource.objects.using(db_alias).all(): source.pre_authentication_flow = flow source.save()
def up(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): cls = apps.get_model('playlister', 'DeezerSettings') if not cls.objects.exists(): row = cls() row.id = 1 row.access_token = None row.save()
def remove_profiles(apps: Apps, schema_editor): """ Remove the profiles for all non-discord users """ Profile = apps.get_model("account", "Profile") for profile in Profile.objects.all(): profile.delete()
def migrate_to_username(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): db_alias = schema_editor.connection.alias UserReputation = apps.get_model("authentik_policies_reputation", "userreputation") for rep in UserReputation.objects.using(db_alias).all(): rep.username = rep.user.username rep.save()
def test_dynamic_load(self): """ Makes a new model at runtime and ensures it goes into the right place. """ old_models = apps.get_models(apps.get_app_config("apps").models_module) # Construct a new model in a new app registry body = {} new_apps = Apps() meta_contents = {"app_label": "apps", "apps": new_apps} meta = type(str("Meta"), tuple(), meta_contents) body["Meta"] = meta body["__module__"] = TotallyNormal.__module__ temp_model = type(str("SouthPonies"), (models.Model,), body) # Make sure it appeared in the right place! self.assertEqual(old_models, apps.get_models(apps.get_app_config("apps").models_module)) with self.assertRaises(LookupError): apps.get_model("apps", "SouthPonies") self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model)
def test_dynamic_load(self): """ Makes a new model at runtime and ensures it goes into the right place. """ old_models = list(apps.get_app_config("apps").get_models()) # Construct a new model in a new app registry body = {} new_apps = Apps(["apps"]) meta_contents = { 'app_label': "apps", 'apps': new_apps, } meta = type("Meta", tuple(), meta_contents) body['Meta'] = meta body['__module__'] = TotallyNormal.__module__ temp_model = type("SouthPonies", (models.Model,), body) # Make sure it appeared in the right place! self.assertEqual(list(apps.get_app_config("apps").get_models()), old_models) with self.assertRaises(LookupError): apps.get_model("apps", "SouthPonies") self.assertEqual(new_apps.get_model("apps", "SouthPonies"), temp_model)
class ProjectState(object): """ Represents the entire project's overall state. This is the item that is passed around - we do it here rather than at the app level so that cross-app FKs/etc. resolve properly. """ def __init__(self, models=None, real_apps=None): self.models = models or {} self.apps = None # Apps to include from main registry, usually unmigrated ones self.real_apps = real_apps or [] def add_model_state(self, model_state): self.models[(model_state.app_label, model_state.name.lower())] = model_state def clone(self): "Returns an exact copy of this ProjectState" return ProjectState( models=dict((k, v.clone()) for k, v in self.models.items()), real_apps=self.real_apps, ) def render(self, include_real=None, ignore_swappable=False, skip_cache=False): "Turns the project state into actual models in a new Apps" if self.apps is None or skip_cache: # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there # are some variables that refer to the Apps object. # FKs/M2Ms from real apps are also not included as they just # mess things up with partial states (due to lack of dependencies) real_models = [] for app_label in self.real_apps: app = global_apps.get_app_config(app_label) for model in app.get_models(): real_models.append(ModelState.from_model(model, exclude_rels=True)) # Populate the app registry with a stub for each application. app_labels = set(model_state.app_label for model_state in self.models.values()) self.apps = Apps([AppConfigStub(label) for label in sorted(self.real_apps + list(app_labels))]) # We keep trying to render the models in a loop, ignoring invalid # base errors, until the size of the unrendered models doesn't # decrease by at least one, meaning there's a base dependency loop/ # missing base. unrendered_models = list(self.models.values()) + real_models while unrendered_models: new_unrendered_models = [] for model in unrendered_models: try: model.render(self.apps) except InvalidBasesError: new_unrendered_models.append(model) if len(new_unrendered_models) == len(unrendered_models): raise InvalidBasesError("Cannot resolve bases for %r\nThis can happen if you are inheriting models from an app with migrations (e.g. contrib.auth)\n in an app with no migrations; see https://docs.djangoproject.com/en/1.7/topics/migrations/#dependencies for more" % new_unrendered_models) unrendered_models = new_unrendered_models # make sure apps has no dangling references if self.apps._pending_lookups: # There's some lookups left. See if we can first resolve them # ourselves - sometimes fields are added after class_prepared is sent for lookup_model, operations in self.apps._pending_lookups.items(): try: model = self.apps.get_model(lookup_model[0], lookup_model[1]) except LookupError: if "%s.%s" % (lookup_model[0], lookup_model[1]) == settings.AUTH_USER_MODEL and ignore_swappable: continue # Raise an error with a best-effort helpful message # (only for the first issue). Error message should look like: # "ValueError: Lookup failed for model referenced by # field migrations.Book.author: migrations.Author" raise ValueError("Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}".format( field=operations[0][1], model=lookup_model, )) else: do_pending_lookups(model) try: return self.apps finally: if skip_cache: self.apps = None @classmethod def from_apps(cls, apps): "Takes in an Apps and returns a ProjectState matching it" app_models = {} for model in apps.get_models(include_swapped=True): model_state = ModelState.from_model(model) app_models[(model_state.app_label, model_state.name.lower())] = model_state return cls(app_models) def __eq__(self, other): if set(self.models.keys()) != set(other.models.keys()): return False if set(self.real_apps) != set(other.real_apps): return False return all(model == other.models[key] for key, model in self.models.items()) def __ne__(self, other): return not (self == other)
class ProjectState(object): """ Represents the entire project's overall state. This is the item that is passed around - we do it here rather than at the app level so that cross-app FKs/etc. resolve properly. """ def __init__(self, models=None, real_apps=None): self.models = models or {} self.apps = None # Apps to include from main registry, usually unmigrated ones self.real_apps = real_apps or [] def add_model_state(self, model_state): self.models[(model_state.app_label, model_state.name.lower())] = model_state def clone(self): "Returns an exact copy of this ProjectState" return ProjectState( models=dict((k, v.clone()) for k, v in self.models.items()), real_apps=self.real_apps, ) def render(self, include_real=None): "Turns the project state into actual models in a new Apps" if self.apps is None: # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there # are some variables that refer to the Apps object. real_models = [] for app_label in self.real_apps: app = global_apps.get_app_config(app_label) for model in app.get_models(): real_models.append(ModelState.from_model(model)) # Populate the app registry with a stub for each application. app_labels = set(model_state.app_label for model_state in self.models.values()) self.apps = Apps([AppConfigStub(label) for label in sorted(self.real_apps + list(app_labels))]) # We keep trying to render the models in a loop, ignoring invalid # base errors, until the size of the unrendered models doesn't # decrease by at least one, meaning there's a base dependency loop/ # missing base. unrendered_models = list(self.models.values()) + real_models while unrendered_models: new_unrendered_models = [] for model in unrendered_models: try: model.render(self.apps) except InvalidBasesError: new_unrendered_models.append(model) if len(new_unrendered_models) == len(unrendered_models): raise InvalidBasesError("Cannot resolve bases for %r" % new_unrendered_models) unrendered_models = new_unrendered_models # make sure apps has no dangling references if self.apps._pending_lookups: # There's some lookups left. See if we can first resolve them # ourselves - sometimes fields are added after class_prepared is sent for lookup_model, operations in self.apps._pending_lookups.items(): try: model = self.apps.get_model(lookup_model[0], lookup_model[1]) except LookupError: # If the lookup failed to something that looks like AUTH_USER_MODEL, # give a better error message about how you can't change it (#22563) extra_message = "" if "%s.%s" % (lookup_model[0], lookup_model[1]) == settings.AUTH_USER_MODEL: extra_message = ( "\nThe missing model matches AUTH_USER_MODEL; if you've changed the value of this" + "\nsetting after making a migration, be aware that this is not supported. If you" + "\nchange AUTH_USER_MODEL you must delete and recreate migrations for its app." ) # Raise an error with a best-effort helpful message # (only for the first issue). Error message should look like: # "ValueError: Lookup failed for model referenced by # field migrations.Book.author: migrations.Author" raise ValueError("Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}{extra_message}".format( field = operations[0][1], model = lookup_model, extra_message = extra_message, )) else: do_pending_lookups(model) return self.apps @classmethod def from_apps(cls, apps): "Takes in an Apps and returns a ProjectState matching it" app_models = {} for model in apps.get_models(): model_state = ModelState.from_model(model) app_models[(model_state.app_label, model_state.name.lower())] = model_state return cls(app_models) def __eq__(self, other): if set(self.models.keys()) != set(other.models.keys()): return False if set(self.real_apps) != set(other.real_apps): return False return all(model == other.models[key] for key, model in self.models.items()) def __ne__(self, other): return not (self == other)