Ejemplo n.º 1
0
 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:
                     app_label = "%s.%s" % (lookup_model[0], lookup_model[1])
                     if app_label == 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"
                     msg = "Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}"
                     raise ValueError(msg.format(field=operations[0][1], model=lookup_model))
                 else:
                     do_pending_lookups(model)
     try:
         return self.apps
     finally:
         if skip_cache:
             self.apps = None
Ejemplo n.º 2
0
 def render(self):
     "Turns the project state into actual models in a new Apps"
     if self.apps is None:
         self.apps = Apps()
         # Populate the app registry with a stub for each application.
         app_labels = set(model_state.app_label for model_state in self.models.values())
         app_configs = [AppConfigStub(label) for label in sorted(app_labels)]
         self.apps.populate_apps(app_configs)
         self.apps.populate_models()
         # 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())
         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
     return self.apps
def get_models():
    result = []
    apps = Apps(settings.INSTALLED_APPS)
    for app_config in apps.get_app_configs():
        try:
            import_module('%s.comments' % app_config.module.__name__)
        except ImportError:
            pass
        except Exception as e:
            raise e

        for attr_name in dir(app_config.models_module):
            attr = getattr(app_config.models_module, attr_name)
            if isinstance(attr, ModelBase) and attr.__module__ == '%s.models' % app_config.module.__name__:
                result.append(attr)
    return result
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
 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)
Ejemplo n.º 7
0
    def test_create(self):
        """
        Tests making a ProjectState from an Apps
        """

        new_apps = Apps(["migrations"])

        class Author(models.Model):
            name = models.CharField(max_length=255)
            bio = models.TextField()
            age = models.IntegerField(blank=True, null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                unique_together = ["name", "bio"]
                index_together = ["bio", "age"]

        class AuthorProxy(Author):
            class Meta:
                app_label = "migrations"
                apps = new_apps
                proxy = True
                ordering = ["name"]

        class SubAuthor(Author):
            width = models.FloatField(null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            title = models.CharField(max_length=1000)
            author = models.ForeignKey(Author)
            contributors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                verbose_name = "tome"
                db_table = "test_tome"

        class Food(models.Model):

            food_mgr = FoodManager('a', 'b')
            food_qs = FoodQuerySet.as_manager()
            food_no_mgr = NoMigrationFoodManager('x', 'y')

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class FoodNoManagers(models.Model):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class FoodNoDefaultManager(models.Model):

            food_no_mgr = NoMigrationFoodManager('x', 'y')
            food_mgr = FoodManager('a', 'b')
            food_qs = FoodQuerySet.as_manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        mgr1 = FoodManager('a', 'b')
        mgr2 = FoodManager('x', 'y', c=3, d=4)

        class FoodOrderedManagers(models.Model):
            # The managers on this model should be ordered by their creation
            # counter and not by the order in model body

            food_no_mgr = NoMigrationFoodManager('x', 'y')
            food_mgr2 = mgr2
            food_mgr1 = mgr1

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        sub_author_state = project_state.models['migrations', 'subauthor']
        book_state = project_state.models['migrations', 'book']
        food_state = project_state.models['migrations', 'food']
        food_no_managers_state = project_state.models['migrations',
                                                      'foodnomanagers']
        food_no_default_manager_state = project_state.models[
            'migrations', 'foodnodefaultmanager']
        food_order_manager_state = project_state.models['migrations',
                                                        'foodorderedmanagers']

        self.assertEqual(author_state.app_label, "migrations")
        self.assertEqual(author_state.name, "Author")
        self.assertEqual([x for x, y in author_state.fields],
                         ["id", "name", "bio", "age"])
        self.assertEqual(author_state.fields[1][1].max_length, 255)
        self.assertEqual(author_state.fields[2][1].null, False)
        self.assertEqual(author_state.fields[3][1].null, True)
        self.assertEqual(
            author_state.options, {
                "unique_together": {("name", "bio")},
                "index_together": {("bio", "age")}
            })
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields],
                         ["id", "title", "author", "contributors"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.fields[3][1].__class__.__name__,
                         "ManyToManyField")
        self.assertEqual(book_state.options, {
            "verbose_name": "tome",
            "db_table": "test_tome"
        })
        self.assertEqual(book_state.bases, (models.Model, ))

        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {
            "proxy": True,
            "ordering": ["name"]
        })
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))

        self.assertEqual(sub_author_state.app_label, "migrations")
        self.assertEqual(sub_author_state.name, "SubAuthor")
        self.assertEqual(len(sub_author_state.fields), 2)
        self.assertEqual(sub_author_state.bases, ("migrations.author", ))

        # The default manager is used in migrations
        self.assertEqual([name for name, mgr in food_state.managers],
                         ['food_mgr'])
        self.assertTrue(
            all(
                isinstance(name, six.text_type)
                for name, mgr in food_state.managers))
        self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2))

        # No explicit managers defined. Migrations will fall back to the default
        self.assertEqual(food_no_managers_state.managers, [])

        # food_mgr is used in migration but isn't the default mgr, hence add the
        # default
        self.assertEqual(
            [name for name, mgr in food_no_default_manager_state.managers],
            ['food_no_mgr', 'food_mgr'])
        self.assertTrue(
            all(
                isinstance(name, six.text_type)
                for name, mgr in food_no_default_manager_state.managers))
        self.assertEqual(
            food_no_default_manager_state.managers[0][1].__class__,
            models.Manager)
        self.assertIsInstance(food_no_default_manager_state.managers[1][1],
                              FoodManager)

        self.assertEqual(
            [name for name, mgr in food_order_manager_state.managers],
            ['food_mgr1', 'food_mgr2'])
        self.assertTrue(
            all(
                isinstance(name, six.text_type)
                for name, mgr in food_order_manager_state.managers))
        self.assertEqual(
            [mgr.args for name, mgr in food_order_manager_state.managers],
            [('a', 'b', 1, 2), ('x', 'y', 3, 4)])
Ejemplo n.º 8
0
 def setUp(self):
     self.apps = Apps(['migrations.related_models_app'])
Ejemplo n.º 9
0
 class Meta:
     abstract = True
     apps = Apps()
Ejemplo n.º 10
0
 def test_wait_for_apps_ready_checks_for_exception(self):
     app_reg = Apps()
     app_reg.ready_event.set()
     # thread.is_alive() is False if it's not started.
     dead_thread = threading.Thread()
     self.assertFalse(self.reloader.wait_for_apps_ready(app_reg, dead_thread))
Ejemplo n.º 11
0
 class Meta:
     apps = Apps()
     app_label = "channels"
     db_table = "django_channels"
Ejemplo n.º 12
0
 class Meta:
     verbose_name = 'a model created on the fly'
     app_label = 'my_great_app'
     apps = Apps()
Ejemplo n.º 13
0
def handle_django_settings(filename):
    '''Attempts to load a Django project and get package dependencies from
    settings.

    Tested using Django 1.4 and 1.8. Not sure if some nuances are missed in
    the other versions.
    '''
    old_sys_path = sys.path[:]
    dirpath = os.path.dirname(filename)
    project = os.path.basename(dirpath)
    cwd = os.getcwd()
    project_path = os.path.normpath(os.path.join(dirpath, '..'))
    if project_path not in sys.path:
        sys.path.insert(0, project_path)
    os.chdir(project_path)

    project_settings = '{}.settings'.format(project)
    os.environ['DJANGO_SETTINGS_MODULE'] = project_settings

    try:
        import django
        # Sanity
        django.setup = lambda: False
    except ImportError:
        log.error('Found Django settings, but Django is not installed.')
        return

    log.warn('Loading Django Settings (Using {}): {}'.format(
        django.get_version(), filename))

    from django.conf import LazySettings

    installed_apps = set()
    settings_imports = set()

    try:
        settings = LazySettings()
        settings._setup()
        for k, v in vars(settings._wrapped).items():
            if k not in _excluded_settings and re.match(r'^[A-Z_]+$', k):
                # log.debug('Scanning Django setting: %s', k)
                scan_django_settings(v, settings_imports)

        # Manually scan INSTALLED_APPS since the broad scan won't include
        # strings without a period in it .
        for app in getattr(settings, 'INSTALLED_APPS', []):
            if hasattr(app, '__file__') and getattr(app, '__file__'):
                imp, _ = utils.import_path_from_file(getattr(app, '__file__'))
                installed_apps.add(imp)
            else:
                installed_apps.add(app)
    except Exception as e:
        log.error('Could not load Django settings: %s', e)
        log.debug('', exc_info=True)
        return

    if not installed_apps or not settings_imports:
        log.error('Got empty settings values from Django settings.')

    try:
        from django.apps.registry import apps, Apps, AppRegistryNotReady
        # Django doesn't like it when the initial instance of `apps` is reused,
        # but it has to be populated before other instances can be created.
        if not apps.apps_ready:
            apps.populate(installed_apps)
        else:
            apps = Apps(installed_apps)

        start = time.time()
        while True:
            try:
                for app in apps.get_app_configs():
                    installed_apps.add(app.name)
            except AppRegistryNotReady:
                if time.time() - start > 10:
                    raise Exception('Bail out of waiting for Django')
                log.debug('Waiting for apps to load...')
                continue
            break
    except Exception as e:
        log.debug('Could not use AppConfig: {}'.format(e))

    # Restore before sub scans can occur
    sys.path[:] = old_sys_path
    os.chdir(cwd)

    for item in settings_imports:
        need_scan = item.startswith(_filescan_modules)
        yield ('django', item, project_path if need_scan else None)

    for app in installed_apps:
        need_scan = app.startswith(project)
        yield ('django', app, project_path if need_scan else None)
Ejemplo n.º 14
0
 class Meta:
     apps = Apps()
     app_label = 'migrations'
     db_table = 'django_migrations'
Ejemplo n.º 15
0
    def test_dangling_references_throw_error(self):
        new_apps = Apps()

        class Author(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Publisher(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)
            publisher = models.ForeignKey(Publisher, models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Magazine(models.Model):
            authors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        # Make a valid ProjectState and render it
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Author))
        project_state.add_model(ModelState.from_model(Publisher))
        project_state.add_model(ModelState.from_model(Book))
        project_state.add_model(ModelState.from_model(Magazine))
        self.assertEqual(len(project_state.apps.get_models()), 4)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "The field migrations.Book.author was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model 'author'.\n"
            "The field migrations.Book.publisher was declared with a lazy reference "
            "to 'migrations.publisher', but app 'migrations' doesn't provide model 'publisher'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And another with ManyToManyField.
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Magazine))
        msg = (
            "The field migrations.Magazine.authors was declared with a lazy reference "
            "to 'migrations.author\', but app 'migrations' doesn't provide model 'author'.\n"
            "The field migrations.Magazine_authors.author was declared with a lazy reference "
            "to \'migrations.author\', but app 'migrations' doesn't provide model 'author'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And now with multiple models and multiple fields.
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "The field migrations.Book.author was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model 'author'.\n"
            "The field migrations.Book.publisher was declared with a lazy reference "
            "to 'migrations.publisher', but app 'migrations' doesn't provide model 'publisher'.\n"
            "The field migrations.Magazine.authors was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model 'author'.\n"
            "The field migrations.Magazine_authors.author was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model 'author'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps
Ejemplo n.º 16
0
    def test_field_deconstruction(self):
        test_apps = Apps()

        class Foo(PolymorphicModel):
            foo = PolymorphicTypeField('self', on_delete=models.CASCADE)

            class Meta:
                apps = test_apps
                app_label = 'polymodels'

        class Bar(models.Model):
            foo = PolymorphicTypeField('Foo', on_delete=models.CASCADE)
            foo_null = PolymorphicTypeField(Foo,
                                            on_delete=models.CASCADE,
                                            null=True)
            foo_default = PolymorphicTypeField(
                Foo,
                on_delete=models.CASCADE,
                default=get_content_type(Foo).pk)

            class Meta:
                apps = test_apps
                app_label = 'polymodels'

        def django_version_agnostic(deconstruction):
            # As of Django 1.9+ on_delete is a required parameter and
            # doesn't default to models.CASCADE anymore.
            if django.VERSION >= (1, 9):
                deconstruction['on_delete'] = models.CASCADE
            return deconstruction

        self.assertDeconstructionEqual(
            Foo._meta.get_field('foo'),
            ('foo', 'django.db.models.fields.related.ForeignKey', [],
             django_version_agnostic(
                 {
                     'to': 'contenttypes.ContentType',
                     'related_name': '+',
                     'default': ContentTypeReference('polymodels', 'foo'),
                 })))
        self.assertDeconstructionEqual(
            Bar._meta.get_field('foo'),
            ('foo', 'django.db.models.fields.related.ForeignKey', [],
             django_version_agnostic(
                 {
                     'to': 'contenttypes.ContentType',
                     'related_name': '+',
                     'default': ContentTypeReference('polymodels', 'foo'),
                 })))
        self.assertDeconstructionEqual(
            Bar._meta.get_field('foo_null'),
            ('foo_null', 'django.db.models.fields.related.ForeignKey', [],
             django_version_agnostic({
                 'to': 'contenttypes.ContentType',
                 'related_name': '+',
                 'null': True,
             })))
        self.assertDeconstructionEqual(
            Bar._meta.get_field('foo_default'),
            ('foo_default', 'django.db.models.fields.related.ForeignKey', [],
             django_version_agnostic({
                 'to': 'contenttypes.ContentType',
                 'related_name': '+',
                 'default': get_content_type(Foo).pk,
             })))
Ejemplo n.º 17
0
 def _remake_table(self, model, create_fields=[], delete_fields=[], alter_fields=[], rename_fields=[], override_uniques=None):
     """
     Shortcut to transform a model from old_model into new_model
     """
     # Work out the new fields dict / mapping
     body = dict((f.name, f) for f in model._meta.local_fields)
     mapping = dict((f.column, f.column) for f in model._meta.local_fields)
     # If any of the new or altered fields is introducing a new PK,
     # remove the old one
     restore_pk_field = None
     if any(f.primary_key for f in create_fields) or any(n.primary_key for o, n in alter_fields):
         for name, field in list(body.items()):
             if field.primary_key:
                 field.primary_key = False
                 restore_pk_field = field
                 if field.auto_created:
                     del body[name]
                     del mapping[field.column]
     # Add in any created fields
     for field in create_fields:
         body[field.name] = field
         # If there's a default, insert it into the copy map
         if field.has_default():
             mapping[field.column] = self.connection.ops.quote_parameter(
                 field.get_default()
             )
     # Add in any altered fields
     for (old_field, new_field) in alter_fields:
         del body[old_field.name]
         del mapping[old_field.column]
         body[new_field.name] = new_field
         mapping[new_field.column] = old_field.column
     # Remove any deleted fields
     for field in delete_fields:
         del body[field.name]
         del mapping[field.column]
     # Work inside a new app registry
     apps = Apps()
     # Construct a new model for the new state
     meta_contents = {
         'app_label': model._meta.app_label,
         'db_table': model._meta.db_table + "__new",
         'unique_together': model._meta.unique_together if override_uniques is None else override_uniques,
         'apps': apps,
     }
     meta = type("Meta", tuple(), meta_contents)
     body['Meta'] = meta
     body['__module__'] = model.__module__
     temp_model = type(model._meta.object_name, model.__bases__, body)
     # Create a new table with that format
     self.create_model(temp_model)
     # Copy data from the old table
     field_maps = list(mapping.items())
     self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % (
         self.quote_name(temp_model._meta.db_table),
         ', '.join(x for x, y in field_maps),
         ', '.join(y for x, y in field_maps),
         self.quote_name(model._meta.db_table),
     ))
     # Delete the old table
     self.delete_model(model)
     # Rename the new to the old
     self.alter_db_table(model, temp_model._meta.db_table, model._meta.db_table)
     # Run deferred SQL on correct table
     for sql in self.deferred_sql:
         self.execute(sql.replace(temp_model._meta.db_table, model._meta.db_table))
     self.deferred_sql = []
     # Fix any PK-removed field
     if restore_pk_field:
         restore_pk_field.primary_key = True
Ejemplo n.º 18
0
from __future__ import unicode_literals

from django.apps.registry import Apps
from django.db import models

# We're testing app registry presence on load, so this is handy.

new_apps = Apps(['apps'])


class TotallyNormal(models.Model):
    name = models.CharField(max_length=255)


class SoAlternative(models.Model):
    name = models.CharField(max_length=255)

    class Meta:
        apps = new_apps
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
    def test_dangling_references_throw_error(self):
        new_apps = Apps()

        class Author(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Publisher(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)
            publisher = models.ForeignKey(Publisher, models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Magazine(models.Model):
            authors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        # Make a valid ProjectState and render it
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Author))
        project_state.add_model(ModelState.from_model(Publisher))
        project_state.add_model(ModelState.from_model(Book))
        project_state.add_model(ModelState.from_model(Magazine))
        self.assertEqual(len(project_state.apps.get_models()), 4)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: migrations.Book.author)\n"
            "  migrations.publisher (referred to by fields: migrations.Book.publisher)"
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And another with ManyToManyField.
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Magazine))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: "
            "migrations.Magazine.authors, migrations.Magazine_authors.author)")
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And now with multiple models and multiple fields.
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: migrations.Book.author, "
            "migrations.Magazine.authors, migrations.Magazine_authors.author)\n"
            "  migrations.publisher (referred to by fields: migrations.Book.publisher)"
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps
Ejemplo n.º 21
0
 class Meta:
     # Disable auto loading of this model as we load it on our own
     apps = Apps()
     verbose_name = 'úñí©óðé µóðéø'
     verbose_name_plural = 'úñí©óðé µóðéøß'
Ejemplo n.º 22
0
    def test_create(self):
        """
        Tests making a ProjectState from an Apps
        """

        new_apps = Apps(["migrations"])

        class Author(models.Model):
            name = models.CharField(max_length=255)
            bio = models.TextField()
            age = models.IntegerField(blank=True, null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                unique_together = ["name", "bio"]

        class AuthorProxy(Author):
            class Meta:
                app_label = "migrations"
                apps = new_apps
                proxy = True
                ordering = ["name"]

        class Book(models.Model):
            title = models.CharField(max_length=1000)
            author = models.ForeignKey(Author)
            contributors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                verbose_name = "tome"
                db_table = "test_tome"

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        book_state = project_state.models['migrations', 'book']

        self.assertEqual(author_state.app_label, "migrations")
        self.assertEqual(author_state.name, "Author")
        self.assertEqual([x for x, y in author_state.fields],
                         ["id", "name", "bio", "age"])
        self.assertEqual(author_state.fields[1][1].max_length, 255)
        self.assertEqual(author_state.fields[2][1].null, False)
        self.assertEqual(author_state.fields[3][1].null, True)
        self.assertEqual(author_state.options,
                         {"unique_together": {("name", "bio")}})
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields],
                         ["id", "title", "author", "contributors"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.fields[3][1].__class__.__name__,
                         "ManyToManyField")
        self.assertEqual(book_state.options, {
            "verbose_name": "tome",
            "db_table": "test_tome"
        })
        self.assertEqual(book_state.bases, (models.Model, ))

        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {
            "proxy": True,
            "ordering": ["name"]
        })
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))
Ejemplo n.º 23
0
 class Meta:
     # Disable auto loading of this model as we load it on our own
     apps = Apps()
Ejemplo n.º 24
0
 class Meta:
     apps = Apps()
     app_label = "channels"
     db_table = "django_channel_groups"
     unique_together = [["group", "channel"]]
Ejemplo n.º 25
0
    def _remake_table(self,
                      model,
                      create_fields=[],
                      delete_fields=[],
                      alter_fields=[],
                      override_uniques=None):
        """
        Shortcut to transform a model from old_model into new_model
        """
        # Work out the new fields dict / mapping
        body = dict((f.name, f) for f in model._meta.local_fields)
        # Since mapping might mix column names and default values,
        # its values must be already quoted.
        mapping = dict((f.column, self.quote_name(f.column))
                       for f in model._meta.local_fields)
        # This maps field names (not columns) for things like unique_together
        rename_mapping = {}
        # If any of the new or altered fields is introducing a new PK,
        # remove the old one
        restore_pk_field = None
        if any(f.primary_key
               for f in create_fields) or any(n.primary_key
                                              for o, n in alter_fields):
            for name, field in list(body.items()):
                if field.primary_key:
                    field.primary_key = False
                    restore_pk_field = field
                    if field.auto_created:
                        del body[name]
                        del mapping[field.column]
        # Add in any created fields
        for field in create_fields:
            body[field.name] = field
            # If there's a default, insert it into the copy map
            if field.has_default():
                mapping[field.column] = self.quote_value(
                    self.effective_default(field))
        # Add in any altered fields
        for (old_field, new_field) in alter_fields:
            del body[old_field.name]
            del mapping[old_field.column]
            body[new_field.name] = new_field
            mapping[new_field.column] = self.quote_name(old_field.column)
            rename_mapping[old_field.name] = new_field.name
        # Remove any deleted fields
        for field in delete_fields:
            del body[field.name]
            del mapping[field.column]
            # Remove any implicit M2M tables
            if isinstance(
                    field,
                    ManyToManyField) and field.rel.through._meta.auto_created:
                return self.delete_model(field.rel.through)
        # Work inside a new app registry
        apps = Apps()

        # Provide isolated instances of the fields to the new model body
        # Instantiating the new model with an alternate db_table will alter
        # the internal references of some of the provided fields.
        body = copy.deepcopy(body)

        # Work out the new value of unique_together, taking renames into
        # account
        if override_uniques is None:
            override_uniques = [[rename_mapping.get(n, n) for n in unique]
                                for unique in model._meta.unique_together]

        # Construct a new model for the new state
        meta_contents = {
            'app_label': model._meta.app_label,
            'db_table': model._meta.db_table + "__new",
            'unique_together': override_uniques,
            'apps': apps,
        }
        meta = type("Meta", tuple(), meta_contents)
        body['Meta'] = meta
        body['__module__'] = model.__module__

        temp_model = type(model._meta.object_name, model.__bases__, body)
        # Create a new table with that format. We remove things from the
        # deferred SQL that match our table name, too
        self.deferred_sql = [
            x for x in self.deferred_sql if model._meta.db_table not in x
        ]
        self.create_model(temp_model)
        # Copy data from the old table
        field_maps = list(mapping.items())
        self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % (
            self.quote_name(temp_model._meta.db_table),
            ', '.join(self.quote_name(x) for x, y in field_maps),
            ', '.join(y for x, y in field_maps),
            self.quote_name(model._meta.db_table),
        ))
        # Delete the old table
        self.delete_model(model, handle_autom2m=False)
        # Rename the new to the old
        self.alter_db_table(temp_model, temp_model._meta.db_table,
                            model._meta.db_table)
        # Run deferred SQL on correct table
        for sql in self.deferred_sql:
            self.execute(
                sql.replace(temp_model._meta.db_table, model._meta.db_table))
        self.deferred_sql = []
        # Fix any PK-removed field
        if restore_pk_field:
            restore_pk_field.primary_key = True
Ejemplo n.º 26
0
    def _remake_table(self,
                      model,
                      create_field=None,
                      delete_field=None,
                      alter_field=None):
        """
        Shortcut to transform a model from old_model into new_model

        This follows the correct procedure to perform non-rename or column
        addition operations based on SQLite's documentation

        https://www.sqlite.org/lang_altertable.html#caution

        The essential steps are:
          1. Create a table with the updated definition called "new__app_model"
          2. Copy the data from the existing "app_model" table to the new table
          3. Drop the "app_model" table
          4. Rename the "new__app_model" table to "app_model"
          5. Restore any index of the previous "app_model" table.
        """

        # Self-referential fields must be recreated rather than copied from
        # the old model to ensure their remote_field.field_name doesn't refer
        # to an altered field.
        def is_self_referential(f):
            return f.is_relation and f.remote_field.model is model

        # Work out the new fields dict / mapping
        body = {
            f.name: f.clone() if is_self_referential(f) else f
            for f in model._meta.local_concrete_fields
        }
        # Since mapping might mix column names and default values,
        # its values must be already quoted.
        mapping = {
            f.column: self.quote_name(f.column)
            for f in model._meta.local_concrete_fields
        }
        # This maps field names (not columns) for things like unique_together
        rename_mapping = {}
        # If any of the new or altered fields is introducing a new PK,
        # remove the old one
        restore_pk_field = None
        if getattr(create_field, 'primary_key', False) or (
                alter_field and getattr(alter_field[1], 'primary_key', False)):
            for name, field in list(body.items()):
                if field.primary_key:
                    field.primary_key = False
                    restore_pk_field = field
                    if field.auto_created:
                        del body[name]
                        del mapping[field.column]
        # Add in any created fields
        if create_field:
            body[create_field.name] = create_field
            # Choose a default and insert it into the copy map
            if not create_field.many_to_many and create_field.concrete:
                mapping[create_field.column] = self.quote_value(
                    self.effective_default(create_field))
        # Add in any altered fields
        if alter_field:
            old_field, new_field = alter_field
            body.pop(old_field.name, None)
            mapping.pop(old_field.column, None)
            body[new_field.name] = new_field
            if old_field.null and not new_field.null:
                case_sql = "coalesce(%(col)s, %(default)s)" % {
                    'col': self.quote_name(old_field.column),
                    'default': self.quote_value(
                        self.effective_default(new_field))
                }
                mapping[new_field.column] = case_sql
            else:
                mapping[new_field.column] = self.quote_name(old_field.column)
            rename_mapping[old_field.name] = new_field.name
        # Remove any deleted fields
        if delete_field:
            del body[delete_field.name]
            del mapping[delete_field.column]
            # Remove any implicit M2M tables
            if delete_field.many_to_many and delete_field.remote_field.through._meta.auto_created:
                return self.delete_model(delete_field.remote_field.through)
        # Work inside a new app registry
        apps = Apps()

        # Work out the new value of unique_together, taking renames into
        # account
        unique_together = [[rename_mapping.get(n, n) for n in unique]
                           for unique in model._meta.unique_together]

        # Work out the new value for index_together, taking renames into
        # account
        index_together = [[rename_mapping.get(n, n) for n in index]
                          for index in model._meta.index_together]

        indexes = model._meta.indexes
        if delete_field:
            indexes = [
                index for index in indexes
                if delete_field.name not in index.fields
            ]

        constraints = list(model._meta.constraints)

        # Provide isolated instances of the fields to the new model body so
        # that the existing model's internals aren't interfered with when
        # the dummy model is constructed.
        body_copy = copy.deepcopy(body)

        # Construct a new model with the new fields to allow self referential
        # primary key to resolve to. This model won't ever be materialized as a
        # table and solely exists for foreign key reference resolution purposes.
        # This wouldn't be required if the schema editor was operating on model
        # states instead of rendered models.
        meta_contents = {
            'app_label': model._meta.app_label,
            'db_table': model._meta.db_table,
            'unique_together': unique_together,
            'index_together': index_together,
            'indexes': indexes,
            'constraints': constraints,
            'apps': apps,
        }
        meta = type("Meta", (), meta_contents)
        body_copy['Meta'] = meta
        body_copy['__module__'] = model.__module__
        type(model._meta.object_name, model.__bases__, body_copy)

        # Construct a model with a renamed table name.
        body_copy = copy.deepcopy(body)
        meta_contents = {
            'app_label': model._meta.app_label,
            'db_table': 'new__%s' % strip_quotes(model._meta.db_table),
            'unique_together': unique_together,
            'index_together': index_together,
            'indexes': indexes,
            'constraints': constraints,
            'apps': apps,
        }
        meta = type("Meta", (), meta_contents)
        body_copy['Meta'] = meta
        body_copy['__module__'] = model.__module__
        new_model = type('New%s' % model._meta.object_name, model.__bases__,
                         body_copy)

        # Create a new table with the updated schema.
        self.create_model(new_model)

        # Copy data from the old table into the new table
        self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % (
            self.quote_name(new_model._meta.db_table),
            ', '.join(self.quote_name(x) for x in mapping),
            ', '.join(mapping.values()),
            self.quote_name(model._meta.db_table),
        ))

        # Delete the old table to make way for the new
        self.delete_model(model, handle_autom2m=False)

        # Rename the new table to take way for the old
        self.alter_db_table(
            new_model,
            new_model._meta.db_table,
            model._meta.db_table,
            disable_constraints=False,
        )

        # Run deferred SQL on correct table
        for sql in self.deferred_sql:
            self.execute(sql)
        self.deferred_sql = []
        # Fix any PK-removed field
        if restore_pk_field:
            restore_pk_field.primary_key = True
Ejemplo n.º 27
0
    def _remake_table(self,
                      model,
                      create_field=None,
                      delete_field=None,
                      alter_field=None):
        """
        Shortcut to transform a model from old_model into new_model

        The essential steps are:
          1. rename the model's existing table, e.g. "app_model" to "app_model__old"
          2. create a table with the updated definition called "app_model"
          3. copy the data from the old renamed table to the new table
          4. delete the "app_model__old" table
        """

        # Self-referential fields must be recreated rather than copied from
        # the old model to ensure their remote_field.field_name doesn't refer
        # to an altered field.
        def is_self_referential(f):
            return f.is_relation and f.remote_field.model is model

        # Work out the new fields dict / mapping
        body = {
            f.name: f.clone() if is_self_referential(f) else f
            for f in model._meta.local_concrete_fields
        }
        # Since mapping might mix column names and default values,
        # its values must be already noted.
        mapping = {
            f.column: self.note_name(f.column)
            for f in model._meta.local_concrete_fields
        }
        # This maps field names (not columns) for things like unique_together
        rename_mapping = {}
        # If any of the new or altered fields is introducing a new PK,
        # remove the old one
        restore_pk_field = None
        if getattr(create_field, 'primary_key', False) or (
                alter_field and getattr(alter_field[1], 'primary_key', False)):
            for name, field in list(body.items()):
                if field.primary_key:
                    field.primary_key = False
                    restore_pk_field = field
                    if field.auto_created:
                        del body[name]
                        del mapping[field.column]
        # Add in any created fields
        if create_field:
            body[create_field.name] = create_field
            # Choose a default and insert it into the copy map
            if not create_field.many_to_many and create_field.concrete:
                mapping[create_field.column] = self.note_value(
                    self.effective_default(create_field))
        # Add in any altered fields
        if alter_field:
            old_field, new_field = alter_field
            body.pop(old_field.name, None)
            mapping.pop(old_field.column, None)
            body[new_field.name] = new_field
            if old_field.null and not new_field.null:
                case_sql = "coalesce(%(col)s, %(default)s)" % {
                    'col': self.note_name(old_field.column),
                    'default': self.note_value(
                        self.effective_default(new_field))
                }
                mapping[new_field.column] = case_sql
            else:
                mapping[new_field.column] = self.note_name(old_field.column)
            rename_mapping[old_field.name] = new_field.name
        # Remove any deleted fields
        if delete_field:
            del body[delete_field.name]
            del mapping[delete_field.column]
            # Remove any implicit M2M tables
            if delete_field.many_to_many and delete_field.remote_field.through._meta.auto_created:
                return self.delete_model(delete_field.remote_field.through)
        # Work inside a new app registry
        apps = Apps()

        # Provide isolated instances of the fields to the new model body so
        # that the existing model's internals aren't interfered with when
        # the dummy model is constructed.
        body = copy.deepcopy(body)

        # Work out the new value of unique_together, taking renames into
        # account
        unique_together = [[rename_mapping.get(n, n) for n in unique]
                           for unique in model._meta.unique_together]

        # Work out the new value for index_together, taking renames into
        # account
        index_together = [[rename_mapping.get(n, n) for n in index]
                          for index in model._meta.index_together]

        indexes = model._meta.indexes
        if delete_field:
            indexes = [
                index for index in indexes
                if delete_field.name not in index.fields
            ]

        # Construct a new model for the new state
        meta_contents = {
            'app_label': model._meta.app_label,
            'db_table': model._meta.db_table,
            'unique_together': unique_together,
            'index_together': index_together,
            'indexes': indexes,
            'apps': apps,
        }
        meta = type("Meta", tuple(), meta_contents)
        body['Meta'] = meta
        body['__module__'] = model.__module__

        temp_model = type(model._meta.object_name, model.__bases__, body)

        # We need to modify model._meta.db_table, but everything explodes
        # if the change isn't reversed before the end of this method. This
        # context manager helps us avoid that situation.
        @contextlib.contextmanager
        def altered_table_name(model, temporary_table_name):
            original_table_name = model._meta.db_table
            model._meta.db_table = temporary_table_name
            yield
            model._meta.db_table = original_table_name

        with altered_table_name(model, model._meta.db_table + "__old"):
            # Rename the old table to make way for the new
            self.alter_db_table(model, temp_model._meta.db_table,
                                model._meta.db_table)

            # Create a new table with the updated schema. We remove things
            # from the deferred SQL that match our table name, too
            self.deferred_sql = [
                x for x in self.deferred_sql
                if temp_model._meta.db_table not in x
            ]
            self.create_model(temp_model)

            # Copy data from the old table into the new table
            field_maps = list(mapping.items())
            self.execute("INSERT INTO %s (%s) SELECT %s FROM %s" % (
                self.note_name(temp_model._meta.db_table),
                ', '.join(self.note_name(x) for x, y in field_maps),
                ', '.join(y for x, y in field_maps),
                self.note_name(model._meta.db_table),
            ))

            # Delete the old table
            self.delete_model(model, handle_autom2m=False)

        # Run deferred SQL on correct table
        for sql in self.deferred_sql:
            self.execute(sql)
        self.deferred_sql = []
        # Fix any PK-removed field
        if restore_pk_field:
            restore_pk_field.primary_key = True
Ejemplo n.º 28
0
 def test_wait_for_apps_ready_without_exception(self):
     app_reg = Apps()
     app_reg.ready_event.set()
     thread = mock.MagicMock()
     thread.is_alive.return_value = True
     self.assertTrue(self.reloader.wait_for_apps_ready(app_reg, thread))
Ejemplo n.º 29
0
 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:
                     app_label = "%s.%s" % (lookup_model[0],
                                            lookup_model[1])
                     if app_label == 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"
                     msg = "Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}"
                     raise ValueError(
                         msg.format(field=operations[0][1],
                                    model=lookup_model))
                 else:
                     do_pending_lookups(model)
     try:
         return self.apps
     finally:
         if skip_cache:
             self.apps = None
Ejemplo n.º 30
0
 class Meta:
     app_label = "migrations"
     apps = Apps()
Ejemplo n.º 31
0
from django.apps.registry import Apps
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

# Because we want to test creation and deletion of these as separate things,
# these models are all inserted into a separate Apps so the main test
# runner doesn't migrate them.

new_apps = Apps()


class Author(models.Model):
    name = models.CharField(max_length=255)
    height = models.PositiveIntegerField(null=True, blank=True)

    class Meta:
        apps = new_apps


class AuthorWithM2M(models.Model):
    name = models.CharField(max_length=255)

    class Meta:
        apps = new_apps


class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=100, db_index=True)
    pub_date = models.DateTimeField()
Ejemplo n.º 32
0
 def enable(self):
     self.old_apps = Options.default_apps
     apps = Apps(self.installed_apps)
     setattr(Options, 'default_apps', apps)
     return apps
Ejemplo n.º 33
0
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):
        self.models = models or {}
        self.apps = None

    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())
        )

    def render(self):
        "Turns the project state into actual models in a new Apps"
        if self.apps is None:
            self.apps = Apps()
            # Populate the app registry with a stub for each application.
            app_labels = set(model_state.app_label for model_state in self.models.values())
            app_configs = [AppConfigStub(label) for label in sorted(app_labels)]
            self.apps.populate_apps(app_configs)
            self.apps.populate_models()
            # 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())
            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
        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
        return all(model == other.models[key] for key, model in self.models.items())

    def __ne__(self, other):
        return not (self == other)
Ejemplo n.º 34
0
 class Meta:
     apps = Apps()
     app_label = "migrations"
     db_table = "django_migrations"
Ejemplo n.º 35
0
 def test_singleton_master(self):
     """
     Ensures that only one master registry can exist.
     """
     with self.assertRaises(RuntimeError):
         Apps(installed_apps=None)
Ejemplo n.º 36
0
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):
        "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 "%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)
        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)
Ejemplo n.º 37
0
    def test_add_relations(self):
        """
        #24573 - Adding relations to existing models should reload the
        referenced models too.
        """
        new_apps = Apps()

        class A(models.Model):
            class Meta:
                app_label = 'something'
                apps = new_apps

        class B(A):
            class Meta:
                app_label = 'something'
                apps = new_apps

        class C(models.Model):
            class Meta:
                app_label = 'something'
                apps = new_apps

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        project_state.add_model(ModelState.from_model(C))

        project_state.apps  # We need to work with rendered models

        old_state = project_state.clone()
        model_a_old = old_state.apps.get_model('something', 'A')
        model_b_old = old_state.apps.get_model('something', 'B')
        model_c_old = old_state.apps.get_model('something', 'C')
        # Check that the relations between the old models are correct
        self.assertIs(
            model_a_old._meta.get_field('b').related_model, model_b_old)
        self.assertIs(
            model_b_old._meta.get_field('a_ptr').related_model, model_a_old)

        operation = AddField(
            'c', 'to_a',
            models.OneToOneField(
                'something.A',
                models.CASCADE,
                related_name='from_c',
            ))
        operation.state_forwards('something', project_state)
        model_a_new = project_state.apps.get_model('something', 'A')
        model_b_new = project_state.apps.get_model('something', 'B')
        model_c_new = project_state.apps.get_model('something', 'C')

        # Check that all models have changed
        self.assertIsNot(model_a_old, model_a_new)
        self.assertIsNot(model_b_old, model_b_new)
        self.assertIsNot(model_c_old, model_c_new)
        # Check that the relations between the old models still hold
        self.assertIs(
            model_a_old._meta.get_field('b').related_model, model_b_old)
        self.assertIs(
            model_b_old._meta.get_field('a_ptr').related_model, model_a_old)
        # Check that the relations between the new models correct
        self.assertIs(
            model_a_new._meta.get_field('b').related_model, model_b_new)
        self.assertIs(
            model_b_new._meta.get_field('a_ptr').related_model, model_a_new)
        self.assertIs(
            model_a_new._meta.get_field('from_c').related_model, model_c_new)
        self.assertIs(
            model_c_new._meta.get_field('to_a').related_model, model_a_new)
Ejemplo n.º 38
0
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)
Ejemplo n.º 39
0
 def render(self, include_real=None, ignore_swappable=False):
     "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 "%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)
     return self.apps