def app_model_error(model_key):
     try:
         apps.get_app_config(model_key[0])
         model_error = "app '%s' doesn't provide model '%s'" % model_key
     except LookupError:
         model_error = "app '%s' isn't installed" % model_key[0]
     return model_error
Beispiel #2
0
 def ask_initial(self, app_label):
     """Should we create an initial migration for the app?"""
     # If it was specified on the command line, definitely true
     if app_label in self.specified_apps:
         return True
     # Otherwise, we look to see if it has a migrations module
     # without any Python files in it, apart from __init__.py.
     # Apps from the new app template will have these; the Python
     # file check will ensure we skip South ones.
     try:
         app_config = apps.get_app_config(app_label)
     except LookupError:  # It's a fake app.
         return self.defaults.get("ask_initial", False)
     migrations_import_path, _ = MigrationLoader.migrations_module(
         app_config.label)
     if migrations_import_path is None:
         # It's an application with migrations disabled.
         return self.defaults.get("ask_initial", False)
     try:
         migrations_module = importlib.import_module(migrations_import_path)
     except ImportError:
         return self.defaults.get("ask_initial", False)
     else:
         # getattr() needed on PY36 and older (replace with attribute access).
         if getattr(migrations_module, "__file__", None):
             filenames = os.listdir(
                 os.path.dirname(migrations_module.__file__))
         elif hasattr(migrations_module, "__path__"):
             if len(migrations_module.__path__) > 1:
                 return False
             filenames = os.listdir(list(migrations_module.__path__)[0])
         return not any(
             x.endswith(".py") for x in filenames if x != "__init__.py")
Beispiel #3
0
def parse_apps_and_model_labels(labels):
    """
    Parse a list of "app_label.ModelName" or "app_label" strings into actual
    objects and return a two-element tuple:
        (set of model classes, set of app_configs).
    Raise a CommandError if some specified models or apps don't exist.
    """
    apps = set()
    models = set()

    for label in labels:
        if '.' in label:
            try:
                model = installed_apps.get_model(label)
            except LookupError:
                raise CommandError('Unknown model: %s' % label)
            models.add(model)
        else:
            try:
                app_config = installed_apps.get_app_config(label)
            except LookupError as e:
                raise CommandError(str(e))
            apps.add(app_config)

    return models, apps
Beispiel #4
0
    def handle(self, *app_labels, **options):
        include_deployment_checks = options['deploy']
        if options['list_tags']:
            self.stdout.write('\n'.join(
                sorted(registry.tags_available(include_deployment_checks))))
            return

        if app_labels:
            app_configs = [
                apps.get_app_config(app_label) for app_label in app_labels
            ]
        else:
            app_configs = None

        tags = options['tags']
        if tags:
            try:
                invalid_tag = next(
                    tag for tag in tags
                    if not checks.tag_exists(tag, include_deployment_checks))
            except StopIteration:
                # no invalid tags
                pass
            else:
                raise CommandError(
                    'There is no system check with the "%s" tag.' %
                    invalid_tag)

        self.check(
            app_configs=app_configs,
            tags=tags,
            display_num_errors=True,
            include_deployment_checks=include_deployment_checks,
            fail_level=getattr(checks, options['fail_level']),
        )
Beispiel #5
0
    def basedir(self):
        migrations_package_name, _ = MigrationLoader.migrations_module(self.migration.app_label)

        if migrations_package_name is None:
            raise ValueError(
                "Server can't create migrations for app '%s' because "
                "migrations have been disabled via the MIGRATION_MODULES "
                "setting." % self.migration.app_label
            )

        # See if we can import the migrations module directly
        try:
            migrations_module = import_module(migrations_package_name)
        except ImportError:
            pass
        else:
            try:
                return module_dir(migrations_module)
            except ValueError:
                pass

        # Alright, see if it's a direct submodule of the app
        app_config = apps.get_app_config(self.migration.app_label)
        maybe_app_name, _, migrations_package_basename = migrations_package_name.rpartition(".")
        if app_config.name == maybe_app_name:
            return os.path.join(app_config.path, migrations_package_basename)

        # In case of using MIGRATION_MODULES setting and the custom package
        # doesn't exist, create one, starting from an existing package
        existing_dirs, missing_dirs = migrations_package_name.split("."), []
        while existing_dirs:
            missing_dirs.insert(0, existing_dirs.pop(-1))
            try:
                base_module = import_module(".".join(existing_dirs))
            except (ImportError, ValueError):
                continue
            else:
                try:
                    base_dir = module_dir(base_module)
                except ValueError:
                    continue
                else:
                    break
        else:
            raise ValueError(
                "Could not locate an appropriate location to create "
                "migrations package %s. Make sure the toplevel "
                "package exists and can be imported." %
                migrations_package_name)

        final_dir = os.path.join(base_dir, *missing_dirs)
        if not os.path.isdir(final_dir):
            os.makedirs(final_dir)
        for missing_dir in missing_dirs:
            base_dir = os.path.join(base_dir, missing_dir)
            with open(os.path.join(base_dir, "__init__.py"), "w"):
                pass

        return final_dir
Beispiel #6
0
 def migrations_module(cls, app_label):
     """
     Return the path to the migrations module for the specified app_label
     and a boolean indicating if the module is specified in
     settings.MIGRATION_MODULE.
     """
     if app_label in settings.MIGRATION_MODULES:
         return settings.MIGRATION_MODULES[app_label], True
     else:
         app_package_name = apps.get_app_config(app_label).name
         return '%s.%s' % (app_package_name, MIGRATIONS_MODULE_NAME), False
 def set_options(self, **options):
     """
     Set instance variables based on an options dict
     """
     self.interactive = options['interactive']
     self.verbosity = options['verbosity']
     self.symlink = options['link']
     self.clear = options['clear']
     self.dry_run = options['dry_run']
     ignore_patterns = options['ignore_patterns']
     if options['use_default_ignore_patterns']:
         ignore_patterns += apps.get_app_config(
             'staticfiles').ignore_patterns
     self.ignore_patterns = list(set(ignore_patterns))
     self.post_process = options['post_process']
Beispiel #8
0
 def handle(self, *app_labels, **options):
     from server.apps import apps
     try:
         app_configs = [
             apps.get_app_config(app_label) for app_label in app_labels
         ]
     except (LookupError, ImportError) as e:
         raise CommandError(
             "%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
     output = []
     for app_config in app_configs:
         app_output = self.handle_app_config(app_config, **options)
         if app_output:
             output.append(app_output)
     return '\n'.join(output)
Beispiel #9
0
    def get_context_data(self, **kwargs):
        model_name = self.kwargs['model_name']
        # Get the model class.
        try:
            app_config = apps.get_app_config(self.kwargs['app_label'])
        except LookupError:
            raise Http404(_("App %(app_label)r not found") % self.kwargs)
        try:
            model = app_config.get_model(model_name)
        except LookupError:
            raise Http404(
                _("Model %(model_name)r not found in app %(app_label)r") %
                self.kwargs)

        opts = model._meta

        title, body, metadata = utils.parse_docstring(model.__doc__)
        title = title and utils.parse_rst(title, 'model',
                                          _('model:') + model_name)
        body = body and utils.parse_rst(body, 'model',
                                        _('model:') + model_name)

        # Gather fields/field descriptions.
        fields = []
        for field in opts.fields:
            # ForeignKey is a special case since the field will actually be a
            # descriptor that returns the other object
            if isinstance(field, models.ForeignKey):
                data_type = field.remote_field.model.__name__
                app_label = field.remote_field.model._meta.app_label
                verbose = utils.parse_rst(
                    (_("the related `%(app_label)s.%(data_type)s` object") % {
                        'app_label': app_label,
                        'data_type': data_type,
                    }),
                    'model',
                    _('model:') + data_type,
                )
            else:
                data_type = get_readable_field_data_type(field)
                verbose = field.verbose_name
            fields.append({
                'name': field.name,
                'data_type': data_type,
                'verbose': verbose or '',
                'help_text': field.help_text,
            })

        # Gather many-to-many fields.
        for field in opts.many_to_many:
            data_type = field.remote_field.model.__name__
            app_label = field.remote_field.model._meta.app_label
            verbose = _("related `%(app_label)s.%(object_name)s` objects") % {
                'app_label': app_label,
                'object_name': data_type,
            }
            fields.append({
                'name':
                "%s.all" % field.name,
                "data_type":
                'List',
                'verbose':
                utils.parse_rst(
                    _("all %s") % verbose, 'model',
                    _('model:') + opts.model_name),
            })
            fields.append({
                'name':
                "%s.count" % field.name,
                'data_type':
                'Integer',
                'verbose':
                utils.parse_rst(
                    _("number of %s") % verbose, 'model',
                    _('model:') + opts.model_name),
            })

        methods = []
        # Gather model methods.
        for func_name, func in model.__dict__.items():
            if inspect.isfunction(func):
                try:
                    for exclude in MODEL_METHODS_EXCLUDE:
                        if func_name.startswith(exclude):
                            raise StopIteration
                except StopIteration:
                    continue
                verbose = func.__doc__
                verbose = verbose and (utils.parse_rst(
                    utils.trim_docstring(verbose), 'model',
                    _('model:') + opts.model_name))
                # If a method has no arguments, show it as a 'field', otherwise
                # as a 'method with arguments'.
                if func_has_no_args(func) and not func_accepts_kwargs(
                        func) and not func_accepts_var_args(func):
                    fields.append({
                        'name': func_name,
                        'data_type': get_return_data_type(func_name),
                        'verbose': verbose or '',
                    })
                else:
                    arguments = get_func_full_args(func)
                    # Join arguments with ', ' and in case of default value,
                    # join it with '='. Use repr() so that strings will be
                    # correctly displayed.
                    print_arguments = ', '.join([
                        '='.join(
                            list(arg_el[:1]) + [repr(el) for el in arg_el[1:]])
                        for arg_el in arguments
                    ])
                    methods.append({
                        'name': func_name,
                        'arguments': print_arguments,
                        'verbose': verbose or '',
                    })

        # Gather related objects
        for rel in opts.related_objects:
            verbose = _("related `%(app_label)s.%(object_name)s` objects") % {
                'app_label': rel.related_model._meta.app_label,
                'object_name': rel.related_model._meta.object_name,
            }
            accessor = rel.get_accessor_name()
            fields.append({
                'name':
                "%s.all" % accessor,
                'data_type':
                'List',
                'verbose':
                utils.parse_rst(
                    _("all %s") % verbose, 'model',
                    _('model:') + opts.model_name),
            })
            fields.append({
                'name':
                "%s.count" % accessor,
                'data_type':
                'Integer',
                'verbose':
                utils.parse_rst(
                    _("number of %s") % verbose, 'model',
                    _('model:') + opts.model_name),
            })
        return super().get_context_data(
            **{
                **kwargs,
                'name': '%s.%s' % (opts.app_label, opts.object_name),
                'summary': title,
                'description': body,
                'fields': fields,
                'methods': methods,
            })
Beispiel #10
0
    def handle(self, *app_labels, **options):
        self.verbosity = options['verbosity']
        self.interactive = options['interactive']
        self.dry_run = options['dry_run']
        self.merge = options['merge']
        self.empty = options['empty']
        self.migration_name = options['name']
        check_changes = options['check_changes']

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                if '.' in app_label:
                    self.stderr.write(
                        "'%s' is not a valid app label. Did you mean '%s'?" % (
                            app_label,
                            app_label.split('.')[-1],
                        ))
                else:
                    self.stderr.write(
                        "App '%s' could not be found. Is it in INSTALLED_APPS?"
                        % app_label)
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        # Raise an error if any migrations are applied before their dependencies.
        consistency_check_labels = {
            config.label
            for config in apps.get_app_configs()
        }
        # Non-default databases are only checked if database routers used.
        aliases_to_check = connections if settings.DATABASE_ROUTERS else [
            DEFAULT_DB_ALIAS
        ]
        for alias in sorted(aliases_to_check):
            connection = connections[alias]
            if (connection.settings_dict['ENGINE'] !=
                    'server.db.backends.dummy' and any(
                        # At least one model must be migrated to the database.
                        router.allow_migrate(connection.alias,
                                             app_label,
                                             model_name=model._meta.object_name
                                             )
                        for app_label in consistency_check_labels for model in
                        apps.get_app_config(app_label).get_models())):
                loader.check_consistent_history(connection)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()

        # If app_labels is specified, filter out conflicting migrations for unspecified apps
        if app_labels:
            conflicts = {
                app_label: conflict
                for app_label, conflict in conflicts.items()
                if app_label in app_labels
            }

        if conflicts and not self.merge:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            raise CommandError(
                "Conflicting migrations detected; multiple leaf nodes in the "
                "migration graph: (%s).\nTo fix them run "
                "'python manage.py makemigrations --merge'" % name_str)

        # If they want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        if self.interactive:
            questioner = InteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        else:
            questioner = NonInteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {app: [Migration("custom", app)] for app in app_labels}
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )

        if not changes:
            # No changes? Tell them.
            if self.verbosity >= 1:
                if app_labels:
                    if len(app_labels) == 1:
                        self.stdout.write("No changes detected in app '%s'" %
                                          app_labels.pop())
                    else:
                        self.stdout.write("No changes detected in apps '%s'" %
                                          ("', '".join(app_labels)))
                else:
                    self.stdout.write("No changes detected")
        else:
            self.write_migration_files(changes)
            if check_changes:
                sys.exit(1)
Beispiel #11
0
    def handle(self, *app_labels, **options):
        format = options['format']
        indent = options['indent']
        using = options['database']
        excludes = options['exclude']
        output = options['output']
        show_traceback = options['traceback']
        use_natural_foreign_keys = options['use_natural_foreign_keys']
        use_natural_primary_keys = options['use_natural_primary_keys']
        use_base_manager = options['use_base_manager']
        pks = options['primary_keys']

        if pks:
            primary_keys = [pk.strip() for pk in pks.split(',')]
        else:
            primary_keys = []

        excluded_models, excluded_apps = parse_apps_and_model_labels(excludes)

        if not app_labels:
            if primary_keys:
                raise CommandError(
                    "You can only use --pks option with one model")
            app_list = OrderedDict.fromkeys(
                app_config for app_config in apps.get_app_configs()
                if app_config.models_module is not None
                and app_config not in excluded_apps)
        else:
            if len(app_labels) > 1 and primary_keys:
                raise CommandError(
                    "You can only use --pks option with one model")
            app_list = OrderedDict()
            for label in app_labels:
                try:
                    app_label, model_label = label.split('.')
                    try:
                        app_config = apps.get_app_config(app_label)
                    except LookupError as e:
                        raise CommandError(str(e))
                    if app_config.models_module is None or app_config in excluded_apps:
                        continue
                    try:
                        model = app_config.get_model(model_label)
                    except LookupError:
                        raise CommandError("Unknown model: %s.%s" %
                                           (app_label, model_label))

                    app_list_value = app_list.setdefault(app_config, [])

                    # We may have previously seen a "all-models" request for
                    # this app (no model qualifier was given). In this case
                    # there is no need adding specific models to the list.
                    if app_list_value is not None:
                        if model not in app_list_value:
                            app_list_value.append(model)
                except ValueError:
                    if primary_keys:
                        raise CommandError(
                            "You can only use --pks option with one model")
                    # This is just an app - no model qualifier
                    app_label = label
                    try:
                        app_config = apps.get_app_config(app_label)
                    except LookupError as e:
                        raise CommandError(str(e))
                    if app_config.models_module is None or app_config in excluded_apps:
                        continue
                    app_list[app_config] = None

        # Check that the serialization format exists; this is a shortcut to
        # avoid collating all the objects and _then_ failing.
        if format not in serializers.get_public_serializer_formats():
            try:
                serializers.get_serializer(format)
            except serializers.SerializerDoesNotExist:
                pass

            raise CommandError("Unknown serialization format: %s" % format)

        def get_objects(count_only=False):
            """
            Collate the objects to be serialized. If count_only is True, just
            count the number of objects to be serialized.
            """
            models = serializers.sort_dependencies(app_list.items())
            for model in models:
                if model in excluded_models:
                    continue
                if model._meta.proxy and model._meta.proxy_for_model not in models:
                    warnings.warn(
                        "%s is a proxy model and won't be serialized." %
                        model._meta.label,
                        category=ProxyModelWarning,
                    )
                if not model._meta.proxy and router.allow_migrate_model(
                        using, model):
                    if use_base_manager:
                        objects = model._base_manager
                    else:
                        objects = model._default_manager

                    queryset = objects.using(using).order_by(
                        model._meta.pk.name)
                    if primary_keys:
                        queryset = queryset.filter(pk__in=primary_keys)
                    if count_only:
                        yield queryset.order_by().count()
                    else:
                        yield from queryset.iterator()

        try:
            self.stdout.ending = None
            progress_output = None
            object_count = 0
            # If dumpdata is outputting to stdout, there is no way to display progress
            if output and self.stdout.isatty() and options['verbosity'] > 0:
                progress_output = self.stdout
                object_count = sum(get_objects(count_only=True))
            stream = open(output, 'w') if output else None
            try:
                serializers.serialize(
                    format,
                    get_objects(),
                    indent=indent,
                    use_natural_foreign_keys=use_natural_foreign_keys,
                    use_natural_primary_keys=use_natural_primary_keys,
                    stream=stream or self.stdout,
                    progress_output=progress_output,
                    object_count=object_count,
                )
            finally:
                if stream:
                    stream.close()
        except Exception as e:
            if show_traceback:
                raise
            raise CommandError("Unable to serialize database: %s" % e)