def test_set_remove(self):
        """Testing ChangeMeta(unique_together) and setting indexes and removing
        one
        """
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                unique_together = [('int_field1', 'char_field1')]

        self.set_base_model(ChangeMetaPlainBaseModel)
        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'unique_together',
                           [('int_field1', 'char_field1'),
                            ('int_field2', 'char_field2')]),
                ChangeMeta('TestModel', 'unique_together',
                           [('int_field1', 'char_field1')])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'unique_together',"
                " [('int_field1', 'char_field1')])"
            ],
            'set_remove')
    def test_append_list(self):
        """Testing ChangeMeta(unique_together) and appending list"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                unique_together = [('int_field1', 'char_field1'),
                                   ('int_field2', 'char_field2')]

        self.set_base_model(ChangeMetaUniqueTogetherBaseModel)
        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'unique_together',
                           [('int_field1', 'char_field1'),
                            ('int_field2', 'char_field2')]),
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'unique_together',"
                " [('int_field1', 'char_field1'),"
                " ('int_field2', 'char_field2')])"
            ],
            'append_list')
    def test_missing_indexes(self):
        """Testing ChangeMeta(unique_together) and old missing indexes"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                unique_together = [('char_field1', 'char_field2')]

        self.set_base_model(ChangeMetaUniqueTogetherBaseModel)

        # Remove the indexes from the database state, to simulate the indexes
        # not being found in the database. The evolution should still work.
        self.database_state.clear_indexes('tests_testmodel')

        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'unique_together',
                           [('char_field1', 'char_field2')])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'unique_together',"
                " [('char_field1', 'char_field2')])"
            ],
            'ignore_missing_indexes',
            rescan_indexes=False)
    def test_upgrade_from_v1_sig_no_indexes(self):
        """Testing ChangeMeta(unique_together) and upgrade from v1 signature
        with no changes and no indexes in database"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                unique_together = [('int_field1', 'char_field1')]

        self.set_base_model(NoUniqueTogetherBaseModel)

        # Pretend this is an older signature with the same unique_together.
        meta = self.start_sig['tests']['TestModel']['meta']
        del meta['__unique_together_applied']
        meta['unique_together'] = DestModel._meta.unique_together

        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'unique_together',
                           [('int_field1', 'char_field1')])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'unique_together',"
                " [('int_field1', 'char_field1')])"
            ],
            'upgrade_from_v1_sig',
            rescan_indexes=False)
    def test_replace_list(self):
        """Testing ChangeMeta(indexes) and replacing list"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                indexes = [
                    Index(fields=['int_field2']),
                ]

        self.set_base_model(ChangeMetaIndexesBaseModel)
        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'indexes',
                           [{'fields': ['int_field2']}])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'indexes',"
                " [{'fields': ['int_field2']}])"
            ],
            'replace_list')
    def test_upgrade_from_v1_sig_with_indexes(self):
        """Testing ChangeMeta(unique_together) and upgrade from v1 signature
        with no changes and with indexes in database"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                unique_together = [('int_field1', 'char_field1')]

        self.set_base_model(ChangeMetaUniqueTogetherBaseModel)

        # Pretend this is an older signature with the same unique_together.
        model_sig = (
            self.start_sig
            .get_app_sig('tests')
            .get_model_sig('TestModel')
        )
        model_sig._unique_together_applied = False

        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta('TestModel', 'unique_together',
                           [('int_field1', 'char_field1')])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'unique_together',"
                " [('int_field1', 'char_field1')])"
            ],
            None,
            rescan_indexes=False)
    def test_removing(self):
        """Testing ChangeMeta(index_together) and removing property"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

        self.set_base_model(IndexTogetherBaseModel)
        self.perform_evolution_tests(
            DestModel, [ChangeMeta('TestModel', 'index_together', [])],
            self.DIFF_TEXT, ["ChangeMeta('TestModel', 'index_together', [])"],
            'removing')
    def test_append_list(self):
        """Testing ChangeMeta(indexes) and appending list"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                indexes = [
                    Index(fields=['int_field1']),
                    Index(fields=['char_field1', '-char_field2'],
                          name='my_custom_index'),
                    Index(fields=['int_field2']),
                ]

        self.set_base_model(ChangeMetaIndexesBaseModel)
        self.perform_evolution_tests(
            DestModel,
            [
                ChangeMeta(
                    'TestModel',
                    'indexes',
                    [
                        {
                            'fields': ['int_field1'],
                        },
                        {
                            'fields': ['char_field1', '-char_field2'],
                            'name': 'my_custom_index',
                        },
                        {
                            'fields': ['int_field2'],
                        },
                    ])
            ],
            self.DIFF_TEXT,
            [
                "ChangeMeta('TestModel', 'indexes',"
                " [{'fields': ['int_field1']},"
                " {'fields': ['char_field1', '-char_field2'],"
                " 'name': 'my_custom_index'},"
                " {'fields': ['int_field2']}])"
            ],
            'append_list')
    def test_replace_list(self):
        """Testing ChangeMeta(index_together) and replacing list"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                index_together = [('int_field2', 'char_field2')]

        self.set_base_model(IndexTogetherBaseModel)
        self.perform_evolution_tests(DestModel, [
            ChangeMeta('TestModel', 'index_together',
                       [('int_field2', 'char_field2')]),
        ], self.DIFF_TEXT, [
            "ChangeMeta('TestModel', 'index_together',"
            " [('int_field2', 'char_field2')])"
        ], 'replace_list')
    def test_setting_from_empty(self):
        """Testing ChangeMeta(index_together) and setting to valid list"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                index_together = [('int_field1', 'char_field1')]

        self.set_base_model(NoIndexTogetherBaseModel)
        self.perform_evolution_tests(DestModel, [
            ChangeMeta('TestModel', 'index_together',
                       [('int_field1', 'char_field1')]),
        ], self.DIFF_TEXT, [
            "ChangeMeta('TestModel', 'index_together',"
            " [('int_field1', 'char_field1')])"
        ], 'setting_from_empty')
    def test_keeping_empty(self):
        """Testing ChangeMeta(index_together) and keeping list empty"""
        class DestModel(models.Model):
            int_field1 = models.IntegerField()
            int_field2 = models.IntegerField()
            char_field1 = models.CharField(max_length=20)
            char_field2 = models.CharField(max_length=40)

            class Meta:
                index_together = []

        self.set_base_model(NoIndexTogetherBaseModel)
        self.perform_evolution_tests(DestModel, [
            ChangeMeta('TestModel', 'index_together', []),
        ],
                                     None,
                                     None,
                                     None,
                                     expect_noop=True)
Example #12
0
    def evolution(self):
        """Return the mutations needed for resolving the diff.

        This will attempt to return a hinted evolution, consisting of a series
        of mutations for each affected application. These mutations will
        convert the database from the original to the target signatures.

        Returns:
            collections.OrderedDict:
            An ordered dictionary of mutations. Each key is an application
            label, and each value is a list of mutations for the application.
        """
        if self._mutations is not None:
            return self._mutations

        mutations = OrderedDict()

        for app_label, app_changes in six.iteritems(self.changed):
            app_sig = self.target_project_sig.get_app_sig(app_label)
            model_changes = app_changes.get('changed', {})
            app_mutations = []

            for model_name, model_change in six.iteritems(model_changes):
                model_sig = app_sig.get_model_sig(model_name)

                # Process the list of added fields for the model.
                for field_name in model_change.get('added', {}):
                    field_sig = model_sig.get_field_sig(field_name)
                    field_type = field_sig.field_type

                    add_params = field_sig.field_attrs.copy()
                    add_params['field_type'] = field_type

                    if (not issubclass(field_type, models.ManyToManyField) and
                        not field_sig.get_attr_value('null')):
                        # This field requires an initial value. Inject either
                        # a suitable initial value or a placeholder that must
                        # be filled in by the developer.
                        add_params['initial'] = \
                            self._get_initial_value(app_label=app_label,
                                                    model_name=model_name,
                                                    field_name=field_name)

                    if field_sig.related_model:
                        add_params['related_model'] = field_sig.related_model

                    app_mutations.append(AddField(
                        model_name=model_name,
                        field_name=field_name,
                        **add_params))

                # Process the list of deleted fields for the model.
                app_mutations += [
                    DeleteField(model_name=model_name,
                                field_name=field_name)
                    for field_name in model_change.get('deleted', [])
                ]

                # Process the list of changed fields for the model.
                field_changes = model_change.get('changed', {})

                for field_name, field_change in six.iteritems(field_changes):
                    field_sig = model_sig.get_field_sig(field_name)
                    changed_attrs = OrderedDict()

                    field_type_changed = 'field_type' in field_change

                    if field_type_changed:
                        # If the field type changes, we're doing a hard
                        # reset on the attributes. We won't be showing the
                        # difference between any other attributes on here.
                        changed_attrs['field_type'] = field_sig.field_type
                        changed_attrs.update(field_sig.field_attrs)
                    else:
                        changed_attrs.update(
                            (attr, field_sig.get_attr_value(attr))
                            for attr in field_change
                        )

                    if ('null' in field_change and
                        not field_sig.get_attr_value('null') and
                        not issubclass(field_sig.field_type,
                                       models.ManyToManyField)):
                        # The field no longer allows null values, meaning an
                        # initial value is required. Inject either a suitable
                        # initial value or a placeholder that must be filled
                        # in by the developer.
                        changed_attrs['initial'] = \
                            self._get_initial_value(app_label=app_label,
                                                    model_name=model_name,
                                                    field_name=field_name)

                    if 'related_model' in field_change:
                        changed_attrs['related_model'] = \
                            field_sig.related_model

                    app_mutations.append(ChangeField(
                        model_name=model_name,
                        field_name=field_name,
                        **changed_attrs))

                # Process the Meta attribute changes for the model.
                meta_changed = model_change.get('meta_changed', [])

                # Check if the Meta.constraints property has any changes.
                # They'll all be assembled into a single ChangeMeta.
                if 'constraints' in meta_changed:
                    app_mutations.append(ChangeMeta(
                        model_name=model_name,
                        prop_name='constraints',
                        new_value=[
                            dict({
                                'type': constraint_sig.type,
                                'name': constraint_sig.name,
                            }, **constraint_sig.attrs)
                            for constraint_sig in model_sig.constraint_sigs
                        ]))

                # Check if the Meta.indexes property has any changes.
                # They'll all be assembled into a single ChangeMeta.
                if 'indexes' in meta_changed:
                    change_meta_indexes = []

                    for index_sig in model_sig.index_sigs:
                        change_meta_index = index_sig.attrs.copy()

                        if index_sig.expressions:
                            change_meta_index['expressions'] = \
                                index_sig.expressions

                        if index_sig.fields:
                            change_meta_index['fields'] = index_sig.fields

                        if index_sig.name:
                            change_meta_index['name'] = index_sig.name

                        change_meta_indexes.append(change_meta_index)

                    app_mutations.append(ChangeMeta(
                        model_name=model_name,
                        prop_name='indexes',
                        new_value=change_meta_indexes))

                # Check Meta.index_together and Meta.unique_together.
                app_mutations += [
                    ChangeMeta(model_name=model_name,
                               prop_name=prop_name,
                               new_value=getattr(model_sig, prop_name) or [])
                    for prop_name in ('index_together', 'unique_together')
                    if prop_name in meta_changed
                ]

            # Process the list of deleted models for the application.
            app_mutations += [
                DeleteModel(model_name=model_name)
                for model_name in app_changes.get('deleted', {})
            ]

            # See if any important details about the app have changed.
            meta_changed = app_changes.get('meta_changed', {})
            app_label_changed = meta_changed.get('app_id', {})
            legacy_app_label_changed = meta_changed.get('legacy_app_label', {})

            if app_label_changed or legacy_app_label_changed:
                app_mutations.append(RenameAppLabel(
                    app_label_changed.get('old', app_sig.app_id),
                    app_label_changed.get('new', app_sig.app_id),
                    legacy_app_label=legacy_app_label_changed.get(
                        'new', app_sig.legacy_app_label)))

            if app_mutations:
                mutations[app_label] = app_mutations

        self._mutations = mutations

        return mutations
from __future__ import unicode_literals

from django_evolution.mutations import AddField, ChangeMeta
from django.db import models

MUTATIONS = [
    AddField('Repository',
             'hooks_uuid',
             models.CharField,
             max_length=32,
             null=True),
    ChangeMeta('Repository', 'unique_together',
               (('name', 'local_site'),
                ('archived_timestamp', 'path', 'local_site'),
                ('hooks_uuid', 'local_site'))),
]
Example #14
0
    def evolution(self):
        "Generate an evolution that would neutralize the diff"
        mutations = {}

        for app_label, app_changes in self.changed.items():
            for model_name, change in app_changes.get('changed', {}).items():
                model_sig = self.current_sig[app_label][model_name]

                for field_name in change.get('added', {}):
                    field_sig = model_sig['fields'][field_name]
                    field_type = field_sig['field_type']

                    add_params = [
                        (key, field_sig[key])
                        for key in field_sig.keys()
                        if key in ATTRIBUTE_DEFAULTS.keys()
                    ]
                    add_params.append(('field_type', field_type))

                    if (field_type is not models.ManyToManyField and
                        not field_sig.get('null', ATTRIBUTE_DEFAULTS['null'])):
                        add_params.append(
                            ('initial',
                             get_initial_value(app_label, model_name,
                                               field_name)))

                    if 'related_model' in field_sig:
                        add_params.append(('related_model',
                                           '%s' % field_sig['related_model']))

                    mutations.setdefault(app_label, []).append(
                        AddField(model_name, field_name, **dict(add_params)))

                for field_name in change.get('deleted', []):
                    mutations.setdefault(app_label, []).append(
                        DeleteField(model_name, field_name))

                changed = change.get('changed', {})

                for field_name, field_change in changed.iteritems():
                    changed_attrs = {}
                    current_field_sig = model_sig['fields'][field_name]

                    for prop in field_change:
                        if prop == 'related_model':
                            changed_attrs[prop] = current_field_sig[prop]
                        else:
                            changed_attrs[prop] = \
                                current_field_sig.get(prop,
                                                      ATTRIBUTE_DEFAULTS[prop])

                    if ('null' in changed_attrs and
                        current_field_sig['field_type'] !=
                            models.ManyToManyField and
                        not current_field_sig.get('null',
                                                  ATTRIBUTE_DEFAULTS['null'])):
                        changed_attrs['initial'] = \
                            get_initial_value(app_label, model_name,
                                              field_name)

                    mutations.setdefault(app_label, []).append(
                        ChangeField(model_name, field_name, **changed_attrs))

                meta_changed = change.get('meta_changed', [])

                for prop_name in ('unique_together', 'index_together'):
                    if prop_name in meta_changed:
                        mutations.setdefault(app_label, []).append(
                            ChangeMeta(model_name,
                                       prop_name,
                                       model_sig['meta'][prop_name]))

            for model_name in app_changes.get('deleted', {}):
                mutations.setdefault(app_label, []).append(
                    DeleteModel(model_name))

        return mutations
Example #15
0
from __future__ import unicode_literals

from django_evolution.mutations import ChangeMeta

MUTATIONS = [
    ChangeMeta('Group', 'unique_together', (('name', 'local_site'), )),
    ChangeMeta('ReviewRequest', 'unique_together',
               (('commit_id', 'repository'), ('changenum', 'repository'),
                ('local_site', 'local_id'))),
]
from django_evolution.mutations import ChangeMeta


MUTATIONS = [
    ChangeMeta('ReviewRequestVisit', 'unique_together',
               [('user', 'review_request')]),
    ChangeMeta('LocalSiteProfile', 'unique_together',
               [('user', 'local_site'), ('profile', 'local_site')]),
]
from __future__ import unicode_literals

from django_evolution.mutations import AddField, ChangeMeta
from django.db import models


MUTATIONS = [
    AddField('ReviewRequestVisit', 'visibility', models.CharField,
             initial='V', max_length=1),
    ChangeMeta('ReviewRequestVisit', 'index_together',
               [('user', 'visibility')])
]
Example #18
0
from django_evolution.mutations import ChangeMeta

MUTATIONS = [
    ChangeMeta('ContentType', 'unique_together', [('app_label', 'model')]),
]
from __future__ import unicode_literals

from django_evolution.mutations import ChangeMeta


MUTATIONS = [
    ChangeMeta('HostingServiceAccount', 'unique_together', []),
]
from __future__ import unicode_literals

from django_evolution.mutations import ChangeMeta


MUTATIONS = [
    ChangeMeta('Permission', 'unique_together',
               [('content_type', 'codename')]),
]
Example #21
0
from __future__ import unicode_literals

from django_evolution.mutations import ChangeMeta

MUTATIONS = [
    ChangeMeta('Repository', 'unique_together',
               (('name', 'local_site'), ('path', 'local_site'))),
]
Example #22
0
    def evolution(self):
        """Return the mutations needed for resolving the diff.

        This will attempt to return a hinted evolution, consisting of a series
        of mutations for each affected application. These mutations will
        convert the database from the original to the target signatures.

        Returns:
            collections.OrderedDict:
            An ordered dictionary of mutations. Each key is an application
            label, and each value is a list of mutations for the application.
        """
        mutations = OrderedDict()

        for app_label, app_changes in six.iteritems(self.changed):
            app_sig = self.target_project_sig.get_app_sig(app_label)
            model_changes = app_changes.get('changed', {})
            app_mutations = []

            for model_name, model_change in six.iteritems(model_changes):
                model_sig = app_sig.get_model_sig(model_name)

                # Process the list of added fields for the model.
                for field_name in model_change.get('added', {}):
                    field_sig = model_sig.get_field_sig(field_name)
                    field_type = field_sig.field_type

                    add_params = field_sig.field_attrs.copy()
                    add_params['field_type'] = field_type

                    if (not issubclass(field_type, models.ManyToManyField)
                            and not field_sig.get_attr_value('null')):
                        # This field requires an initial value. Inject either
                        # a suitable initial value or a placeholder that must
                        # be filled in by the developer.
                        add_params['initial'] = \
                            self._get_initial_value(app_label=app_label,
                                                    model_name=model_name,
                                                    field_name=field_name)

                    if field_sig.related_model:
                        add_params['related_model'] = field_sig.related_model

                    app_mutations.append(
                        AddField(model_name=model_name,
                                 field_name=field_name,
                                 **add_params))

                # Process the list of deleted fields for the model.
                app_mutations += [
                    DeleteField(model_name=model_name, field_name=field_name)
                    for field_name in model_change.get('deleted', [])
                ]

                # Process the list of changed fields for the model.
                field_changes = model_change.get('changed', {})

                for field_name, field_change in six.iteritems(field_changes):
                    field_sig = model_sig.get_field_sig(field_name)
                    changed_attrs = OrderedDict(
                        (attr, field_sig.get_attr_value(attr))
                        for attr in field_change)

                    if ('null' in changed_attrs
                            and not field_sig.get_attr_value('null')
                            and not issubclass(field_sig.field_type,
                                               models.ManyToManyField)):
                        # The field no longer allows null values, meaning an
                        # initial value is required. Inject either a suitable
                        # initial value or a placeholder that must be filled
                        # in by the developer.
                        changed_attrs['initial'] = \
                            self._get_initial_value(app_label=app_label,
                                                    model_name=model_name,
                                                    field_name=field_name)

                    if 'related_model' in field_change:
                        changed_attrs['related_model'] = \
                            field_sig.related_model

                    app_mutations.append(
                        ChangeField(model_name=model_name,
                                    field_name=field_name,
                                    **changed_attrs))

                # Process the Meta attribute changes for the model.
                meta_changed = model_change.get('meta_changed', [])

                # First, check if the Meta.indexes property has any changes.
                # They'll all be assembled into a single ChangeMeta.
                if 'indexes' in meta_changed:
                    change_meta_indexes = []

                    for index_sig in model_sig.index_sigs:
                        change_meta_index = {
                            'fields': index_sig.fields,
                        }

                        if index_sig.name:
                            change_meta_index['name'] = index_sig.name

                        change_meta_indexes.append(change_meta_index)

                    app_mutations.append(
                        ChangeMeta(model_name=model_name,
                                   prop_name='indexes',
                                   new_value=change_meta_indexes))

                # Then check Meta.index_together and Meta.unique_together.
                app_mutations += [
                    ChangeMeta(model_name=model_name,
                               prop_name=prop_name,
                               new_value=getattr(model_sig, prop_name) or [])
                    for prop_name in ('index_together', 'unique_together')
                    if prop_name in meta_changed
                ]

            # Process the list of deleted models for the application.
            app_mutations += [
                DeleteModel(model_name=model_name)
                for model_name in app_changes.get('deleted', {})
            ]

            if app_mutations:
                mutations[app_label] = app_mutations

        return mutations