Exemplo n.º 1
0
    def queue_task(self, task):
        """Queue a task to run during evolution.

        This should only be directly called if working with custom tasks.
        Otherwise, use a more specific queue method.

        Args:
            task (BaseEvolutionTask):
                The task to queue.

        Raises:
            django_evolution.errors.EvolutionTaskAlreadyQueuedError:
                A purge of this app was already queued.

            django_evolution.errors.QueueEvolverTaskError:
                Error queueing a non-duplicate task. Tasks may have already
                been prepared and finalized.

        """
        assert task.id

        if self._tasks_prepared:
            raise QueueEvolverTaskError(
                _('Evolution tasks have already been prepared. New tasks '
                  'cannot be added.'))

        if task.id in self._tasks_by_id:
            raise EvolutionTaskAlreadyQueuedError(
                _('A task with ID "%s" is already queued.') % task.id)

        self._tasks_by_id[task.id] = task
        self._tasks_by_class.setdefault(type(task), []).append(task)
Exemplo n.º 2
0
    def handle(self, *args, **options):
        """Handle the command.

        This will validate the arguments and run through the evolution
        process.

        Args:
            *args (list of unicode):
                Positional arguments passed on the command line.

            **options (dict):
                Options parsed by the argument parser.

        Raises:
            django.core.management.base.CommandError:
                Arguments were invalid or something went wrong. Details are
                in the message.
        """
        if not has_migrate:
            raise CommandError(
                _('migrate is not available on this version of Django. '
                  'Use `syncdb` instead.'))

        if not django_evolution_settings.ENABLED:
            # Run the original migrate command.
            return super(Command, self).handle(*args, **options)

        if options.get('migration_name'):
            raise CommandError(
                _('The migrate command cannot apply a specific migration '
                  'name when Django Evolution is in use. Set '
                  '`DJANGO_EVOLUTION_ENABLED = False` in your settings.py '
                  'to use the original migrate command.'))

        if options.get('fake'):
            raise CommandError(
                _('The migrate command cannot use --fake when Django '
                  'Evolution is in use. Set '
                  '`DJANGO_EVOLUTION_ENABLED = False` in your settings.py '
                  'to use the original migrate command.'))

        app_labels = []

        if options.get('app_label'):
            app_labels.append(options.get('app_label'))

        call_command('evolve',
                     *app_labels,
                     verbosity=options.get('verbosity'),
                     interactive=options.get('interactive'),
                     database=options.get('database'),
                     execute=True)
Exemplo n.º 3
0
            def _on_creating_models(app_label, model_names, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Creating new database models for %(app_label)s '
                          '(%(model_names)s)...\n') % {
                              'app_label': app_label,
                              'model_names': ', '.join(model_names),
                          })
                else:
                    message = (_('Creating new database models for '
                                 '%(app_label)s...\n') % {
                                     'app_label': app_label,
                                 })

                self.stdout.write(message)
Exemplo n.º 4
0
    def add_arguments(self, parser):
        """Add arguments to the command.

        Args:
            parser (object):
                The argument parser to add to.
        """
        parser.add_argument('args',
                            metavar='APP_LABEL',
                            nargs='*',
                            help=_('One or more app labels to evolve.'))
        parser.add_argument(
            '--noinput',
            action='store_false',
            dest='interactive',
            default=True,
            help=_('Automatically says yes to any prompts. When used with '
                   '--execute, this will apply evolutions without first '
                   'asking for confirmation.'))
        parser.add_argument(
            '--hint',
            action='store_true',
            dest='hint',
            default=False,
            help=_('Display sample evolutions covering any new changes made '
                   'to models since the last evolution.'))
        parser.add_argument(
            '--purge',
            action='store_true',
            dest='purge',
            default=False,
            help=_('Purge deleted applications from the evolution history.'))
        parser.add_argument('--sql',
                            action='store_true',
                            dest='compile_sql',
                            default=False,
                            help=_('Display the evolutions as SQL.'))
        parser.add_argument(
            '-w',
            '--write',
            metavar='EVOLUTION_NAME',
            action='store',
            dest='write_evolution_name',
            default=None,
            help=_('Write the generated evolutions to files with the given '
                   'evolution name in each affected app\'s "evolutions" '
                   'paths.'))
        parser.add_argument('-x',
                            '--execute',
                            action='store_true',
                            dest='execute',
                            default=False,
                            help=_('Apply evolutions to the database.'))
        parser.add_argument(
            '--database',
            action='store',
            dest='database',
            help=_('Specify the database containing models to synchronize.'))
Exemplo n.º 5
0
    def _add_tasks(self, app_labels):
        """Add tasks to the evolver, based on the command options.

        This will queue up the applications that need to be evolved, and
        queue up the purging of stale applications if requested.

        Args:
            app_labels (list of unicode):
                The list of app labels to evolve. If this is empty, all
                registered apps will be evolved.
        """
        evolver = self.evolver

        if app_labels:
            # The caller wants to evolve specific apps. Queue each one,
            # handling any invalid app labels in the process.
            try:
                for app_label in app_labels:
                    evolver.queue_evolve_app(get_app(app_label))
            except (ImportError, ImproperlyConfigured) as e:
                raise CommandError(
                    _('%s. Are you sure your INSTALLED_APPS setting is '
                      'correct?') % e)
        else:
            # The caller wants the default behavior of evolving all apps
            # with pending evolutions.
            evolver.queue_evolve_all_apps()

        if self.purge:
            # The caller wants to purge all old stale applications that
            # no longer exist.
            #
            # Note that we don't do this by default, since those apps
            # might not be permanently added to the list of installed apps.
            evolver.queue_purge_old_apps()
Exemplo n.º 6
0
 def _on_applying_migration(migration, **kwargs):
     self.stdout.write(
         _('Applying database migration %(migration_name)s for '
           '%(app_label)s...\n') % {
               'app_label': migration.app_label,
               'migration_name': migration.name,
           })
Exemplo n.º 7
0
    def execute(self, cursor=None, sql_executor=None, **kwargs):
        """Execute the task.

        This will delete any tables owned by the application.

        Args:
            cursor (django.db.backends.util.CursorWrapper, unused):
                The legacy database cursor. This is no longer used.

            sql_executor (django_evolution.utils.sql.SQLExecutor, optional):
                The SQL executor used to run any SQL on the database.

        Raises:
            django_evolution.errors.EvolutionExecutionError:
                The evolution task failed. Details are in the error.
        """
        assert sql_executor

        if self.evolution_required:
            try:
                sql_executor.run_sql(self.sql, execute=True)
            except Exception as e:
                raise EvolutionExecutionError(
                    _('Error purging app "%s": %s')
                    % (self.app_label, e),
                    app_label=self.app_label,
                    detailed_error=six.text_type(e),
                    last_sql_statement=getattr(e, 'last_sql_statement'))
Exemplo n.º 8
0
 def _on_applied_migration(migration, **kwargs):
     self.stdout.write(
         _('Successfully applied database migration '
           '%(migration_name)s for %(app_label)s.\n') % {
               'app_label': migration.app_label,
               'migration_name': migration.name,
           })
Exemplo n.º 9
0
            def _on_created_models(app_label, model_names, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Successfully created new database models for '
                          '%(app_label)s (%(model_names)s).\n') % {
                              'app_label': app_label,
                              'model_names': ', '.join(model_names),
                          })
                else:
                    message = (
                        _('Successfully created new database models for '
                          '%(app_label)s.\n') % {
                              'app_label': app_label,
                          })

                self.stdout.write(message)
Exemplo n.º 10
0
    def handle(self, *args, **options):
        """Handle the command.

        This will validate the arguments and run through the evolution
        process.

        Args:
            *args (list of unicode):
                Positional arguments passed on the command line.

            **options (dict):
                Options parsed by the argument parser.

        Raises:
            django.core.management.base.CommandError:
                Arguments were invalid or something went wrong. Details are
                in the message.
        """
        if not has_syncdb:
            raise CommandError(
                _('syncdb is not available on this version of Django. '
                  'Use `migrate` instead.'))

        if not django_evolution_settings.ENABLED:
            # Run the original syncdb command.
            return super(Command, self).handle(*args, **options)

        call_command('evolve',
                     verbosity=options.get('verbosity'),
                     interactive=options.get('interactive'),
                     database=options.get('database'),
                     execute=True)
Exemplo n.º 11
0
    def _confirm_execute(self):
        """Prompt the user to confirm execution of an evolution.

        This will warn the user of the risks of evolving the database and
        to recommend a backup. It will then prompt for confirmation, returning
        the result.

        Returns:
            bool:
            ``True`` if the user confirmed the execution. ``False`` if the
            execution should be cancelled.
        """
        prompt = self._wrap_paragraphs(
            _('You have requested a database upgrade. This will alter '
              'tables and data currently in the "%s" database, and may '
              'result in IRREVERSABLE DATA LOSS. Upgrades should be '
              '*thoroughly* reviewed and tested prior to execution.\n'
              '\n'
              'MAKE A BACKUP OF YOUR DATABASE BEFORE YOU CONTINUE!\n'
              '\n'
              'Are you sure you want to execute the database upgrade?\n'
              '\n'
              'Type "yes" to continue, or "no" to cancel:') %
            self.evolver.database_name)

        # Note that we must append a space here, rather than above, since the
        # paragraph wrapping logic will strip trailing whitespace.
        return input('%s ' % prompt).lower() == 'yes'
Exemplo n.º 12
0
    def _display_extra_task_details(self):
        """Display some informative state about queued tasks.

        This will list any applications that are already up-tp-date, and
        list whether or not any applications need to be purged.
        """
        # List all applications that appear up-to-date.
        for task in self.evolver.tasks:
            if (isinstance(task, EvolveAppTask)
                    and not task.evolution_required):
                self.stdout.write(
                    _('Application "%s" is up-to-date\n') % task.app_label)

        if self.purge and not self.active_purge_tasks:
            # List whether there are any applications that need to be
            # purged.
            self.stdout.write(_('No applications need to be purged.\n'))
Exemplo n.º 13
0
            def _on_applying_evolution(task, evolutions, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Applying database evolutions for %(app_label)s '
                          '(%(evolution_labels)s)...\n') % {
                              'app_label':
                              task.app_label,
                              'evolution_labels':
                              ', '.join(evolution.label
                                        for evolution in evolutions),
                          })
                else:
                    message = (_('Applying database evolutions for '
                                 '%(app_label)s...\n') % {
                                     'app_label': task.app_label,
                                 })

                self.stdout.write(message)
Exemplo n.º 14
0
            def _on_applied_evolution(task, evolutions, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Successfully applied database evolutions for '
                          '%(app_label)s (%(evolution_labels)s).\n') % {
                              'app_label':
                              task.app_label,
                              'evolution_labels':
                              ', '.join(evolution.label
                                        for evolution in evolutions),
                          })
                else:
                    message = (
                        _('Successfully applied database evolutions for '
                          '%(app_label)s.\n') % {
                              'app_label': task.app_label,
                          })

                self.stdout.write(message)
Exemplo n.º 15
0
    def add_arguments(self, parser):
        """Add arguments to the command.

        Args:
            parser (object):
                The argument parser to add to.
        """
        parser.add_argument(
            'args',
            metavar='APP_LABEL',
            nargs='*',
            help=_('One or more app labels to list evolutions for.'))
Exemplo n.º 16
0
    def __call__(self):
        """Handle calls on this object.

        This will raise an exception stating that the evolution cannot be
        performed.

        Raises:
            django_evolution.errors.EvolutionException:
                An error stating that an explicit initial value must be
                provided in place of this object.
        """
        raise EvolutionException(
            _('Cannot use hinted evolution: Mutation requires a '
              'user-specified value.'))
Exemplo n.º 17
0
    def __call__(self):
        """Handle calls on this object.

        This will raise an exception stating that the evolution cannot be
        performed.

        Raises:
            django_evolution.errors.EvolutionException:
                An error stating that an explicit initial value must be
                provided in place of this object.
        """
        raise EvolutionException(
            _('Cannot use hinted evolution: AddField or ChangeField mutation '
              'for "%s.%s" in "%s" requires user-specified initial value.') %
            (self.model_name, self.field_name, self.app_label))
Exemplo n.º 18
0
    def evolve(self):
        """Perform the evolution.

        This will run through all queued tasks and attempt to apply them in
        a database transaction, tracking each new batch of evolutions as the
        tasks finish.

        This can only be called once per evolver instance.

        Raises:
            django_evolution.errors.EvolutionException:
                Something went wrong during the evolution process. Details
                are in the error message. Note that a more specific exception
                may be raised.

            django_evolution.errors.EvolutionExecutionError:
                A specific evolution task failed. Details are in the error.
        """
        if self.evolved:
            raise EvolutionException(
                _('Evolver.evolve() has already been run once. It cannot be '
                  'run again.'))

        self._prepare_tasks()

        evolving.send(sender=self)

        try:
            new_evolutions = []

            for task_cls, tasks in six.iteritems(self._tasks_by_class):
                # Perform the evolution for the app. This is responsible
                # for raising any exceptions.
                task_cls.execute_tasks(evolver=self, tasks=tasks)

                for task in tasks:
                    new_evolutions += task.new_evolutions

                # Things may have changed, so rescan the database.
                self.database_state.rescan_tables()

            self._save_project_sig(new_evolutions=new_evolutions)
            self.evolved = True
        except Exception as e:
            evolving_failed.send(sender=self, exception=e)
            raise

        evolved.send(sender=self)
Exemplo n.º 19
0
    def _display_available_purges(self):
        """Display the apps that can be purged."""
        purge_tasks = self.active_purge_tasks

        if purge_tasks:
            self.stdout.write(
                ngettext('The following application can be purged:',
                         'The following applications can be purged:',
                         len(purge_tasks)))
            self.stdout.write('\n')

            for purge_task in purge_tasks:
                self.stdout.write('    * %s\n' % purge_task.app_label)

            self.stdout.write('\n')
        elif self.verbosity > 1:
            self.stdout.write(_('No applications need to be purged.\n'))
Exemplo n.º 20
0
    def queue_purge_app(self, app_label):
        """Queue the purging of a Django app.

        Args:
            app_label (unicode):
                The label of the app to purge.

        Raises:
            django_evolution.errors.EvolutionTaskAlreadyQueuedError:
                A purge of this app was already queued.

            django_evolution.errors.QueueEvolverTaskError:
                Error queueing a non-duplicate task. Tasks may have already
                been prepared and finalized.
        """
        try:
            self.queue_task(PurgeAppTask(evolver=self, app_label=app_label))
        except EvolutionTaskAlreadyQueuedError:
            raise EvolutionTaskAlreadyQueuedError(
                _('"%s" is already being tracked for purging') % app_label)
Exemplo n.º 21
0
    def queue_evolve_app(self, app):
        """Queue an evolution of a registered Django app.

        Args:
            app (module):
                The Django app to queue an evolution for.

        Raises:
            django_evolution.errors.EvolutionTaskAlreadyQueuedError:
                An evolution for this app was already queued.

            django_evolution.errors.QueueEvolverTaskError:
                Error queueing a non-duplicate task. Tasks may have already
                been prepared and finalized.
        """
        try:
            self.queue_task(EvolveAppTask(self, app))
        except EvolutionTaskAlreadyQueuedError:
            raise EvolutionTaskAlreadyQueuedError(
                _('"%s" is already being tracked for evolution') %
                get_app_label(app))
Exemplo n.º 22
0
    def add_arguments(self, parser):
        """Add arguments to the command.

        Args:
            parser (object):
                The argument parser to add to.
        """
        parser.add_argument('args',
                            metavar='EVOLUTION_LABEL',
                            nargs='+',
                            help=_('One or more evolution labels to wipe.'))
        parser.add_argument(
            '--noinput',
            action='store_false',
            dest='interactive',
            default=True,
            help='Tells Django to NOT prompt the user for input of any kind.')
        parser.add_argument(
            '--app-label',
            action='store',
            dest='app_label',
            help='The app label the evolution label applies to.')
Exemplo n.º 23
0
    def _save_project_sig(self, new_evolutions):
        """Save the project signature and any new evolutions.

        This will serialize the current modified project signature to the
        database and write any new evolutions, attaching them to the current
        project version.

        This can be called many times for one evolver instance. After the
        first time, the version already saved will simply be updated.

        Args:
            new_evolutions (list of django_evolution.models.Evolution):
                The list of new evolutions to save to the database.

        Raises:
            django_evolution.errors.EvolutionExecutionError:
                There was an error saving to the database.
        """
        version = self.version

        if version is None:
            version = Version(signature=self.project_sig)
            self.version = version

        try:
            version.save(using=self.database_name)

            if new_evolutions:
                for evolution in new_evolutions:
                    evolution.version = version

                Evolution.objects.using(
                    self.database_name).bulk_create(new_evolutions)
        except Exception as e:
            raise EvolutionExecutionError(
                _('Error saving new evolution version information: %s') % e,
                detailed_error=six.text_type(e))
Exemplo n.º 24
0
    def handle(self, *app_labels, **options):
        """Handle the command.

        This will validate the arguments and run through the evolution
        process.

        Args:
            app_labels (list of unicode):
                The app labels to evolve.

            options (dict):
                Options parsed by the argument parser.

        Raises:
            django.core.management.base.CommandError:
                Arguments were invalid or something went wrong. Details are
                in the message.
        """
        if not django_evolution_settings.ENABLED:
            raise CommandError(
                _('Django Evolution is disabled for this project. '
                  'Evolutions cannot be manually run.'))

        self.purge = options['purge']
        self.verbosity = int(options['verbosity'])

        hint = options['hint']
        compile_sql = options['compile_sql']
        database_name = options['database'] or DEFAULT_DB_ALIAS
        execute = options['execute']
        interactive = options['interactive']
        write_evolution_name = options['write_evolution_name']

        if app_labels and self.execute:
            raise CommandError(
                _('Cannot specify an application name when executing '
                  'evolutions.'))

        if write_evolution_name and not hint:
            raise CommandError(_('--write cannot be used without --hint.'))

        import_management_modules()

        try:
            self.evolver = Evolver(database_name=database_name,
                                   hinted=hint,
                                   verbosity=self.verbosity,
                                   interactive=interactive)

            # Figure out what tasks we need to add to the evolver. This
            # must be done before we check any state (as that will finalize
            # the task list).
            self._add_tasks(app_labels)

            # Calculate some information we may need later.
            self.active_purge_tasks = [
                task for task in self.evolver.tasks
                if isinstance(task, PurgeAppTask) and len(task.sql) > 0
            ]

            # Display any additional information on the evolution process
            # the caller may be interested in.
            if self.verbosity > 1:
                self._display_extra_task_details()

            # Simulate the evolutions to make sure that they'll get us to the
            # target database state. This will raise a CommandError with
            # helpful information if the evolutions don't get us there, or
            # if one or more evolutions couldn't be simulated.
            simulated = self._check_simulation()

            if not self.evolver.get_evolution_required():
                if self.verbosity > 0:
                    self.stdout.write(_('No database upgrade required.\n'))
            elif execute:
                if not interactive or self._confirm_execute():
                    self._perform_evolution()
                else:
                    self.stderr.write(_('Database upgrade cancelled.\n'))
            elif compile_sql:
                self._display_compiled_sql()
            else:
                # Be helpful and list any applications that can be purged,
                # and then show any evolution content that may be useful to
                # the user.
                self._display_available_purges()
                self._generate_evolution_contents(write_evolution_name)

                if simulated:
                    if self.verbosity > 0:
                        self.stdout.write(_('Trial upgrade successful!\n'))

                    if not self.evolver.hinted and self.verbosity > 0:
                        self.stdout.write(
                            _('Run `./manage.py evolve --execute` to apply '
                              'the evolution.\n'))
        except EvolutionException as e:
            raise CommandError(six.text_type(e))
Exemplo n.º 25
0
    def _generate_evolution_contents(self, evolution_label=None):
        """Generate the contents of evolution files or hinted evolutions.

        This will grab the contents of either the stored evolution files
        or hinted evolutions (if using ``--hint``) and write them to the
        console or to generated evolution files (if using ``--write``).

        Args:
            evolution_label (unicode, optional):
                The label used as a base for any generated filenames.
                If provided, the filenames will be written to the appropriate
                evolution directories, with a ``.py`` appended.

        Raises:
            django.core.management.base.CommandError:
                An evolution file couldn't be written. Details are in the
                error message.
        """
        evolution_contents = self.evolver.iter_evolution_content()

        if evolution_label:
            # We're writing the hinted evolution files to disk. Notify the user
            # and begin writing.
            verbosity = self.verbosity

            if verbosity > 0:
                self.stdout.write('\n%s\n\n' % self._wrap_paragraphs(
                    _('The following evolution files were written. Verify the '
                      'contents and add them to the SEQUENCE lists in each '
                      '__init__.py.')))

            for task, content in evolution_contents:
                assert hasattr(task, 'app')

                dirname = get_evolutions_path(task.app)
                filename = os.path.join(dirname, '%s.py' % evolution_label)

                if not os.path.exists(dirname):
                    try:
                        os.mkdir(dirname, 0o755)
                    except IOError as e:
                        raise CommandError(
                            _('Unable to create evolutions directory "%s": %s')
                            % (dirname, e))

                try:
                    with open(filename, 'w') as fp:
                        fp.write(content.strip())
                        fp.write('\n')
                except Exception as e:
                    raise CommandError(
                        _('Unable to write evolution file "%s": %s') %
                        (filename, e))

                if verbosity > 0:
                    self.stdout.write('  * %s\n' % os.path.relpath(filename))
        else:
            # We're just going to output the hint content.
            for i, (task, content) in enumerate(evolution_contents):
                assert hasattr(task, 'app_label')

                self.stdout.write('#----- Evolution for %s\n' % task.app_label)
                self.stdout.write(content.strip())
                self.stdout.write('#----------------------\n')

            self.stdout.write('\n')
Exemplo n.º 26
0
class SignatureField(models.TextField):
    """A field for loading and storing project signatures.

    This will handle deserializing any project signatures stored in the
    database, converting them into a
    :py:class:`~django_evolution.signatures.ProjectSignature`, and then
    writing a serialized version back to the database.
    """

    description = _('Signature')

    def contribute_to_class(self, cls, name):
        """Perform operations when added to a class.

        This will listen for when an instance is constructed in order to
        perform some initial work.

        Args:
            cls (type):
                The model class.

            name (str):
                The name of the field.
        """
        super(SignatureField, self).contribute_to_class(cls, name)

        post_init.connect(self._post_init, sender=cls)

    def value_to_string(self, obj):
        """Return a serialized string value from the field.

        Args:
            obj (django.db.models.Model):
                The model instance.

        Returns:
            unicode:
            The serialized string contents.
        """
        return self._dumps(self.value_from_object(obj))

    def to_python(self, value):
        """Return a ProjectSignature value from the field contents.

        Args:
            value (object):
                The current value assigned to the field. This might be
                serialized string content or a
                :py:class:`~django_evolution.signatures.ProjectSignature`
                instance.

        Returns:
            django_evolution.signatures.ProjectSignature:
            The project signature stored in the field.

        Raises:
            django.core.exceptions.ValidationError:
                The field contents are of an unexpected type.
        """
        if not value:
            return ProjectSignature()
        elif isinstance(value, six.string_types):
            if value.startswith('json!'):
                loaded_value = json.loads(value[len('json!'):],
                                          object_pairs_hook=OrderedDict)
            else:
                loaded_value = pickle_loads(value)

            return ProjectSignature.deserialize(loaded_value)
        elif isinstance(value, ProjectSignature):
            return value
        else:
            raise ValidationError('Unsupported serialized signature type %s' %
                                  type(value),
                                  code='invalid',
                                  params={
                                      'value': value,
                                  })

    def get_prep_value(self, value):
        """Return a prepared Python value to work with.

        This simply wraps :py:meth:`to_python`.

        Args:
            value (object):
                The current value assigned to the field. This might be
                serialized string content or a
                :py:class:`~django_evolution.signatures.ProjectSignature`
                instance.

        Returns:
            django_evolution.signatures.ProjectSignature:
            The project signature stored in the field.

        Raises:
            django.core.exceptions.ValidationError:
                The field contents are of an unexpected type.
        """
        return self.to_python(value)

    def get_db_prep_value(self, value, connection, prepared=False):
        """Return a prepared value for use in database operations.

        Args:
            value (object):
                The current value assigned to the field. This might be
                serialized string content or a
                :py:class:`~django_evolution.signatures.ProjectSignature`
                instance.

            connection (django.db.backends.base.BaseDatabaseWrapper):
                The database connection to operate on.

            prepared (bool, optional):
                Whether the value is already prepared for Python.

        Returns:
            unicode:
            The value prepared for database operations.
        """
        if not prepared:
            value = self.get_prep_value(value)

        return self._dumps(value)

    def _post_init(self, instance, **kwargs):
        """Handle the construction of a model instance.

        This will ensure the value set on the field is a valid
        :py:class:`~django_evolution.signatures.ProjectSignature` object.

        Args:
            instance (django.db.models.Model):
                The model instance being constructed.

            **kwargs (dict, unused):
                Additional keyword arguments from the signal.
        """
        setattr(instance, self.attname,
                self.to_python(self.value_from_object(instance)))

    def _dumps(self, data):
        """Serialize the project signature to a string.

        Args:
            data (object):
                The signature data to dump. This might be serialized string
                content or a
                :py:class:`~django_evolution.signatures.ProjectSignature`
                instance.

        Returns:
            unicode:
            The project signature stored in the field.

        Raises:
            TypeError:
                The data provided was not of a supported type.
        """
        if isinstance(data, six.string_types):
            return data
        elif isinstance(data, ProjectSignature):
            serialized_data = data.serialize()
            sig_version = serialized_data['__version__']

            if sig_version >= 2:
                return 'json!%s' % json.dumps(serialized_data)
            else:
                return pickle_dumps(serialized_data)
        else:
            raise TypeError('Unsupported signature type %s' % type(data))
Exemplo n.º 27
0
    def _perform_evolution(self):
        """Perform the evolution.

        This will perform the evolution, based on the options passed to this
        command. Progress on the evolution will be printed to the console.

        Raises:
            django.core.management.base.CommandError:
                The evolution failed.
        """
        evolver = self.evolver
        verbosity = self.verbosity

        if verbosity > 0:

            @receiver(applying_evolution, sender=evolver)
            def _on_applying_evolution(task, evolutions, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Applying database evolutions for %(app_label)s '
                          '(%(evolution_labels)s)...\n') % {
                              'app_label':
                              task.app_label,
                              'evolution_labels':
                              ', '.join(evolution.label
                                        for evolution in evolutions),
                          })
                else:
                    message = (_('Applying database evolutions for '
                                 '%(app_label)s...\n') % {
                                     'app_label': task.app_label,
                                 })

                self.stdout.write(message)

            @receiver(applying_migration, sender=evolver)
            def _on_applying_migration(migration, **kwargs):
                self.stdout.write(
                    _('Applying database migration %(migration_name)s for '
                      '%(app_label)s...\n') % {
                          'app_label': migration.app_label,
                          'migration_name': migration.name,
                      })

            @receiver(creating_models, sender=evolver)
            def _on_creating_models(app_label, model_names, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Creating new database models for %(app_label)s '
                          '(%(model_names)s)...\n') % {
                              'app_label': app_label,
                              'model_names': ', '.join(model_names),
                          })
                else:
                    message = (_('Creating new database models for '
                                 '%(app_label)s...\n') % {
                                     'app_label': app_label,
                                 })

                self.stdout.write(message)

        if verbosity > 1:

            @receiver(applied_evolution, sender=evolver)
            def _on_applied_evolution(task, evolutions, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Successfully applied database evolutions for '
                          '%(app_label)s (%(evolution_labels)s).\n') % {
                              'app_label':
                              task.app_label,
                              'evolution_labels':
                              ', '.join(evolution.label
                                        for evolution in evolutions),
                          })
                else:
                    message = (
                        _('Successfully applied database evolutions for '
                          '%(app_label)s.\n') % {
                              'app_label': task.app_label,
                          })

                self.stdout.write(message)

            @receiver(applied_migration, sender=evolver)
            def _on_applied_migration(migration, **kwargs):
                self.stdout.write(
                    _('Successfully applied database migration '
                      '%(migration_name)s for %(app_label)s.\n') % {
                          'app_label': migration.app_label,
                          'migration_name': migration.name,
                      })

            @receiver(created_models, sender=evolver)
            def _on_created_models(app_label, model_names, **kwargs):
                if verbosity > 2:
                    message = (
                        _('Successfully created new database models for '
                          '%(app_label)s (%(model_names)s).\n') % {
                              'app_label': app_label,
                              'model_names': ', '.join(model_names),
                          })
                else:
                    message = (
                        _('Successfully created new database models for '
                          '%(app_label)s.\n') % {
                              'app_label': app_label,
                          })

                self.stdout.write(message)

        self.stdout.write('\n%s\n\n' % self._wrap_paragraphs(
            _('This may take a while. Please be patient, and DO NOT '
              'cancel the upgrade!')))

        try:
            evolver.evolve()
        except EvolutionException as e:
            self.stderr.write('%s\n' % e)

            if getattr(e, 'last_sql_statement', None):
                self.stderr.write(
                    _('The SQL statement that failed was: %s\n') %
                    (e.last_sql_statement, ))

            raise CommandError(six.text_type(e))

        if verbosity > 0:
            if evolver.installed_new_database:
                self.stdout.write(_('The database creation was successful!\n'))
            else:
                self.stdout.write(_('The database upgrade was successful!\n'))
Exemplo n.º 28
0
    def _check_simulation(self):
        """Check the results of a simulation.

        This will check first if a simulation could even occur (based on
        whether there are raw SQL mutations that are going to be applied). If a
        simulation did occur, information on the simulation results and
        the resulting signature diff will be displayed.

        If a simulation either could not be performed, or was performed and
        succeeded, a result will be returned so that the caller can perform
        additional operations based on that state.

        If a simulation could be performed but failed, this will immediately
        terminate the command with an error message.

        Returns:
            bool:
            ```True`` if the simulation was successful and all changes were
            resolved. ``False`` if a simulation could not be performed due to
            raw SQL mutations.

        Raises:
            django.core.management.base.CommandError:
                A simulation was performed, but changes could not be resolved.
        """
        if not self.evolver.can_simulate():
            self.stdout.write(
                self.style.NOTICE(
                    _('Evolution could not be simulated, possibly due '
                      'to raw SQL mutations\n')))

            return False

        diff = self.evolver.diff_evolutions()

        if diff.is_empty(ignore_apps=not self.purge):
            return True

        if self.evolver.hinted:
            self.stderr.write(
                self._wrap_paragraphs(
                    _('Your models contain changes that Django Evolution '
                      'cannot resolve automatically.\n'
                      '\n'
                      'This is probably due to a currently unimplemented '
                      'mutation type. You will need to manually construct a '
                      'mutation to resolve the remaining changes.')))
        else:
            self.stderr.write(
                self._wrap_paragraphs(
                    _('The stored evolutions do not completely resolve '
                      'all model changes.\n'
                      '\n'
                      'Run `./manage.py evolve --hint` to see a '
                      'suggestion for the changes required.')))

        self.stdout.write('\n\n')
        self.stdout.write(
            self._wrap_paragraphs(
                _('The following are the changes that could not be resolved:'))
        )
        self.stdout.write('\n%s\n' % diff)

        raise CommandError(
            _('Your models contain changes that Django Evolution cannot '
              'resolve automatically.'))