Example #1
0
    def get_list_columns(self):
        """
            Returns a list of tuples with the model field name and formatted
            field name. If `column_list` was set, returns it. Otherwise calls
            `scaffold_list_columns` to generate the list from the model.
        """
        if self.column_list is None:
            columns = self.scaffold_list_columns()

            # Filter excluded columns
            if self.column_exclude_list:
                columns = [c for c in columns
                           if c not in self.column_exclude_list]

            return [(c, self.get_column_name(c)) for c in columns]
        else:
            columns = []

            for c in self.column_list:
                column, path = tools.get_field_with_path(self.model, c)

                if path:
                    # column is in another table, use full path
                    column_name = text_type(c)
                else:
                    # column is in same table, use only model attribute name
                    column_name = column.key

                visible_name = self.get_column_name(column_name)

                # column_name must match column_name in `get_sortable_columns`
                columns.append((column_name, visible_name))

            return columns
Example #2
0
    def get_sortable_columns(self):
        """
            Returns a dictionary of the sortable columns. Key is a model
            field name and value is sort column (for example - attribute).

            If `column_sortable_list` is set, will use it. Otherwise, will call
            `scaffold_sortable_columns` to get them from the model.
        """
        self._sortable_joins = dict()

        if self.column_sortable_list is None:
            return self.scaffold_sortable_columns()
        else:
            result = dict()

            for c in self.column_sortable_list:
                if isinstance(c, tuple):
                    if isinstance(c[1], tuple):
                        column, path = [], []
                        for item in c[1]:
                            column_item, path_item = tools.get_field_with_path(self.model, item)
                            column.append(column_item)
                            path.append(path_item)
                        column_name = c[0]
                    else:
                        column, path = tools.get_field_with_path(self.model, c[1])
                        column_name = c[0]
                else:
                    column, path = tools.get_field_with_path(self.model, c)
                    column_name = text_type(c)

                if path and (hasattr(path[0], 'property') or isinstance(path[0], list)):
                    self._sortable_joins[column_name] = path
                elif path:
                    raise Exception("For sorting columns in a related table, "
                                    "column_sortable_list requires a string "
                                    "like '<relation name>.<column name>'. "
                                    "Failed on: {0}".format(c))
                else:
                    # column is in same table, use only model attribute name
                    if getattr(column, 'key', None) is not None:
                        column_name = column.key

                # column_name must match column_name used in `get_list_columns`
                result[column_name] = column

            return result
Example #3
0
    def _get_default_order(self):
        order = super(ModelView, self)._get_default_order()

        if order is not None:

            attr, joins = tools.get_field_with_path(self.model, order['field'])
            if order['absolute_value']:
                attr = func.abs(attr)
            return attr, joins, order['sort_desc']

        return None
Example #4
0
    def _get_default_order(self):
        order = super(ModelView, self)._get_default_order()

        if order is not None:
            field, direction = order

            attr, joins = tools.get_field_with_path(self.model, field)

            return attr, joins, direction

        return None
Example #5
0
    def _get_default_order(self):
        order = super(ModelView, self)._get_default_order()

        if order is not None:
            field, direction = order

            attr, joins = tools.get_field_with_path(self.model, field)

            return attr, joins, direction

        return None
Example #6
0
    def get_sortable_columns(self):
        """
            Returns a dictionary of the sortable columns. Key is a model
            field name and value is sort column (for example - attribute).

            If `column_sortable_list` is set, will use it. Otherwise, will call
            `scaffold_sortable_columns` to get them from the model.
        """
        self._sortable_joins = dict()

        if self.column_sortable_list is None:
            return self.scaffold_sortable_columns()
        else:
            result = dict()

            for c in self.column_sortable_list:
                if isinstance(c, tuple):
                    column, path = tools.get_field_with_path(self.model, c[1])
                    column_name = c[0]
                else:
                    column, path = tools.get_field_with_path(self.model, c)
                    column_name = text_type(c)

                if path and hasattr(path[0], 'property'):
                    self._sortable_joins[column_name] = path
                elif path:
                    raise Exception("For sorting columns in a related table, "
                                    "column_sortable_list requires a string "
                                    "like '<relation name>.<column name>'. "
                                    "Failed on: {0}".format(c))
                else:
                    # column is in same table, use only model attribute name
                    if getattr(column, 'key', None) is not None:
                        column_name = column.key
                    else:
                        column_name = text_type(c)

                # column_name must match column_name used in `get_list_columns`
                result[column_name] = column

            return result
Example #7
0
    def get_column_names(self, only_columns, excluded_columns):
        """
            Returns a list of tuples with the model field name and formatted
            field name.

            Overridden to handle special columns like InstrumentedAttribute.

            :param only_columns:
                List of columns to include in the results. If not set,
                `scaffold_list_columns` will generate the list from the model.
            :param excluded_columns:
                List of columns to exclude from the results.
        """
        if excluded_columns:
            only_columns = [
                c for c in only_columns if c not in excluded_columns
            ]

        formatted_columns = []
        for c in only_columns:
            try:
                column, path = tools.get_field_with_path(self.model, c)

                if path:
                    # column is a relation (InstrumentedAttribute), use full path
                    column_name = text_type(c)
                else:
                    # column is in same table, use only model attribute name
                    if getattr(column, 'key', None) is not None:
                        column_name = column.key
                    else:
                        column_name = text_type(c)
            except AttributeError:
                # TODO: See ticket #1299 - allow virtual columns. Probably figure out
                # better way to handle it. For now just assume if column was not found - it
                # is virtual and there's column formatter for it.
                column_name = text_type(c)

            visible_name = self.get_column_name(column_name)

            # column_name must match column_name in `get_sortable_columns`
            formatted_columns.append((column_name, visible_name))

        return formatted_columns
Example #8
0
    def get_column_names(self, only_columns, excluded_columns):
        """
            Returns a list of tuples with the model field name and formatted
            field name.

            Overridden to handle special columns like InstrumentedAttribute.

            :param only_columns:
                List of columns to include in the results. If not set,
                `scaffold_list_columns` will generate the list from the model.
            :param excluded_columns:
                List of columns to exclude from the results.
        """
        if excluded_columns:
            only_columns = [c for c in only_columns if c not in excluded_columns]

        formatted_columns = []
        for c in only_columns:
            try:
                column, path = tools.get_field_with_path(self.model, c)

                if path:
                    # column is a relation (InstrumentedAttribute), use full path
                    column_name = text_type(c)
                else:
                    # column is in same table, use only model attribute name
                    if getattr(column, 'key', None) is not None:
                        column_name = column.key
                    else:
                        column_name = text_type(c)
            except AttributeError:
                # TODO: See ticket #1299 - allow virtual columns. Probably figure out
                # better way to handle it. For now just assume if column was not found - it
                # is virtual and there's column formatter for it.
                column_name = text_type(c)

            visible_name = self.get_column_name(column_name)

            # column_name must match column_name in `get_sortable_columns`
            formatted_columns.append((column_name, visible_name))

        return formatted_columns
Example #9
0
    def init_search(self):
        """
            Initialize search. Returns `True` if search is supported for this
            view.

            For SQLAlchemy, this will initialize internal fields: list of
            column objects used for filtering, etc.
        """
        if self.column_searchable_list:
            self._search_fields = []

            for p in self.column_searchable_list:
                attr, joins = tools.get_field_with_path(self.model, p)

                if not attr:
                    raise Exception('Failed to find field for search field: %s' % p)

                for column in tools.get_columns_for_field(attr):
                    self._search_fields.append((column, joins))

        return bool(self.column_searchable_list)
Example #10
0
    def init_search(self):
        """
            Initialize search. Returns `True` if search is supported for this
            view.

            For SQLAlchemy, this will initialize internal fields: list of
            column objects used for filtering, etc.
        """
        if self.column_searchable_list:
            self._search_fields = []

            for p in self.column_searchable_list:
                attr, joins = tools.get_field_with_path(self.model, p)

                if not attr:
                    raise Exception('Failed to find field for search field: %s' % p)

                for column in tools.get_columns_for_field(attr):
                    self._search_fields.append((column, joins))

        return bool(self.column_searchable_list)
Example #11
0
    def get_list_columns(self):
        """
            Returns a list of tuples with the model field name and formatted
            field name. If `column_list` was set, returns it. Otherwise calls
            `scaffold_list_columns` to generate the list from the model.
        """
        if self.column_list is None:
            columns = self.scaffold_list_columns()

            # Filter excluded columns
            if self.column_exclude_list:
                columns = [
                    c for c in columns if c not in self.column_exclude_list
                ]

            return [(c, self.get_column_name(c)) for c in columns]
        else:
            columns = []

            for c in self.column_list:
                column, path = tools.get_field_with_path(self.model, c)

                if path:
                    # column is in another table, use full path
                    column_name = text_type(c)
                else:
                    # column is in same table, use only model attribute name
                    if getattr(column, 'key', None) is not None:
                        column_name = column.key
                    else:
                        column_name = text_type(c)

                visible_name = self.get_column_name(column_name)

                # column_name must match column_name in `get_sortable_columns`
                columns.append((column_name, visible_name))

            return columns
Example #12
0
    def get_column_names(self, only_columns, excluded_columns):
        """
            Returns a list of tuples with the model field name and formatted
            field name.

            Overridden to handle special columns like InstrumentedAttribute.

            :param only_columns:
                List of columns to include in the results. If not set,
                `scaffold_list_columns` will generate the list from the model.
            :param excluded_columns:
                List of columns to exclude from the results.
        """
        if excluded_columns:
            only_columns = [
                c for c in only_columns if c not in excluded_columns
            ]

        formatted_columns = []
        for c in only_columns:
            column, path = tools.get_field_with_path(self.model, c)

            if path:
                # column is a relation (InstrumentedAttribute), use full path
                column_name = text_type(c)
            else:
                # column is in same table, use only model attribute name
                if getattr(column, 'key', None) is not None:
                    column_name = column.key
                else:
                    column_name = text_type(c)

            visible_name = self.get_column_name(column_name)

            # column_name must match column_name in `get_sortable_columns`
            formatted_columns.append((column_name, visible_name))

        return formatted_columns
Example #13
0
    def get_column_names(self, only_columns, excluded_columns):
        """
            Returns a list of tuples with the model field name and formatted
            field name.

            Overridden to handle special columns like InstrumentedAttribute.

            :param only_columns:
                List of columns to include in the results. If not set,
                `scaffold_list_columns` will generate the list from the model.
            :param excluded_columns:
                List of columns to exclude from the results.
        """
        if excluded_columns:
            only_columns = [
                c for c in only_columns if c not in excluded_columns
            ]

        formatted_columns = []
        for c in only_columns:
            column, path = tools.get_field_with_path(self.model, c)

            if path:
                # column is a relation (InstrumentedAttribute), use full path
                column_name = text_type(c)
            else:
                # column is in same table, use only model attribute name
                if getattr(column, 'key', None) is not None:
                    column_name = column.key
                else:
                    column_name = text_type(c)

            visible_name = self.get_column_name(column_name)

            # column_name must match column_name in `get_sortable_columns`
            formatted_columns.append((column_name, visible_name))

        return formatted_columns
Example #14
0
    def scaffold_filters(self, name):
        """
            Return list of enabled filters
        """

        attr, joins = tools.get_field_with_path(self.model, name)

        if attr is None:
            raise Exception('Failed to find field for filter: %s' % name)

        # Figure out filters for related column
        if hasattr(attr, 'property') and hasattr(attr.property, 'direction'):
            filters = []

            for p in self._get_model_iterator(attr.property.mapper.class_):
                if hasattr(p, 'columns'):
                    # TODO: Check for multiple columns
                    column = p.columns[0]

                    if column.foreign_keys or column.primary_key:
                        continue

                    visible_name = '%s / %s' % (self.get_column_name(attr.prop.table.name),
                                                self.get_column_name(p.key))

                    type_name = type(column.type).__name__
                    flt = self.filter_converter.convert(type_name,
                                                        column,
                                                        visible_name)

                    if flt:
                        table = column.table

                        if joins:
                            self._filter_joins[column] = joins
                        elif tools.need_join(self.model, table):
                            self._filter_joins[column] = [table]

                        filters.extend(flt)

            return filters
        else:
            is_hybrid_property = isinstance(attr, ColumnElement)
            if is_hybrid_property:
                column = attr
            else:
                columns = tools.get_columns_for_field(attr)

                if len(columns) > 1:
                    raise Exception('Can not filter more than on one column for %s' % name)

                column = columns[0]

            # If filter related to relation column (represented by
            # relation_name.target_column) we collect here relation name
            joined_column_name = None
            if '.' in name:
                joined_column_name = name.split('.')[0]

            # Join not needed for hybrid properties
            if (not is_hybrid_property and tools.need_join(self.model, column.table) and
                    name not in self.column_labels):
                if joined_column_name:
                    visible_name = '%s / %s / %s' % (
                        joined_column_name,
                        self.get_column_name(column.table.name),
                        self.get_column_name(column.name)
                    )
                else:
                    visible_name = '%s / %s' % (
                        self.get_column_name(column.table.name),
                        self.get_column_name(column.name)
                    )
            else:
                if not isinstance(name, string_types):
                    visible_name = self.get_column_name(name.property.key)
                else:
                    visible_name = self.get_column_name(name)

            type_name = type(column.type).__name__

            flt = self.filter_converter.convert(
                type_name,
                column,
                visible_name,
                options=self.column_choices.get(name),
            )

            key_name = column
            # In case of filter related to relation column filter key
            # must be named with relation name (to prevent following same
            # target column to replace previous)
            if joined_column_name:
                key_name = "{0}.{1}".format(joined_column_name, column)
                for f in flt:
                    f.key_name = key_name

            if joins:
                self._filter_joins[key_name] = joins
            elif not is_hybrid_property and tools.need_join(self.model, column.table):
                self._filter_joins[key_name] = [column.table]

            return flt
Example #15
0
 def _get_default_order(self):
     order = super(ModelView, self)._get_default_order()
     for field, direction in (order or []):
         attr, joins = tools.get_field_with_path(self.model, field)
         yield attr, joins, direction
Example #16
0
    def scaffold_filters(self, name):
        """
            Return list of enabled filters
        """

        attr, joins = tools.get_field_with_path(self.model, name)

        if attr is None:
            raise Exception('Failed to find field for filter: %s' % name)

        # Figure out filters for related column
        if is_relationship(attr):
            filters = []

            for p in self._get_model_iterator(attr.property.mapper.class_):
                if hasattr(p, 'columns'):
                    # TODO: Check for multiple columns
                    column = p.columns[0]

                    if column.foreign_keys or column.primary_key:
                        continue

                    visible_name = '%s / %s' % (self.get_column_name(attr.prop.target.name),
                                                self.get_column_name(p.key))

                    type_name = type(column.type).__name__
                    flt = self.filter_converter.convert(type_name,
                                                        column,
                                                        visible_name)

                    if flt:
                        table = column.table

                        if joins:
                            self._filter_joins[column] = joins
                        elif tools.need_join(self.model, table):
                            self._filter_joins[column] = [table]

                        filters.extend(flt)

            return filters
        else:
            is_hybrid_property = tools.is_hybrid_property(self.model, name)
            if is_hybrid_property:
                column = attr
                if isinstance(name, string_types):
                    column.key = name.split('.')[-1]
            else:
                columns = tools.get_columns_for_field(attr)

                if len(columns) > 1:
                    raise Exception('Can not filter more than on one column for %s' % name)

                column = columns[0]

            # If filter related to relation column (represented by
            # relation_name.target_column) we collect here relation name
            joined_column_name = None
            if isinstance(name, string_types) and '.' in name:
                joined_column_name = name.split('.')[0]

            # Join not needed for hybrid properties
            if (not is_hybrid_property and tools.need_join(self.model, column.table) and
                    name not in self.column_labels):
                if joined_column_name:
                    visible_name = '%s / %s / %s' % (
                        joined_column_name,
                        self.get_column_name(column.table.name),
                        self.get_column_name(column.name)
                    )
                else:
                    visible_name = '%s / %s' % (
                        self.get_column_name(column.table.name),
                        self.get_column_name(column.name)
                    )
            else:
                if not isinstance(name, string_types):
                    visible_name = self.get_column_name(name.property.key)
                else:
                    if self.column_labels and name in self.column_labels:
                        visible_name = self.column_labels[name]
                    else:
                        visible_name = self.get_column_name(name)
                        visible_name = visible_name.replace('.', ' / ')

            type_name = type(column.type).__name__

            flt = self.filter_converter.convert(
                type_name,
                column,
                visible_name,
                options=self.column_choices.get(name),
            )

            key_name = column
            # In case of filter related to relation column filter key
            # must be named with relation name (to prevent following same
            # target column to replace previous)
            if joined_column_name:
                key_name = "{0}.{1}".format(joined_column_name, column)
                for f in flt:
                    f.key_name = key_name

            if joins:
                self._filter_joins[key_name] = joins
            elif not is_hybrid_property and tools.need_join(self.model, column.table):
                self._filter_joins[key_name] = [column.table]

            return flt
Example #17
0
    def scaffold_filters(self, name):
        """
            Return list of enabled filters
        """

        attr, joins = tools.get_field_with_path(self.model, name)

        if attr is None:
            raise Exception('Failed to find field for filter: %s' % name)

        # Figure out filters for related column
        if hasattr(attr, 'property') and hasattr(attr.property, 'direction'):
            filters = []

            for p in self._get_model_iterator(attr.property.mapper.class_):
                if hasattr(p, 'columns'):
                    # TODO: Check for multiple columns
                    column = p.columns[0]

                    if column.foreign_keys or column.primary_key:
                        continue

                    visible_name = '%s / %s' % (self.get_column_name(
                        attr.prop.table.name), self.get_column_name(p.key))

                    type_name = type(column.type).__name__
                    flt = self.filter_converter.convert(
                        type_name, column, visible_name)

                    if flt:
                        table = column.table

                        if joins:
                            self._filter_joins[column] = joins
                        elif tools.need_join(self.model, table):
                            self._filter_joins[column] = [table]

                        filters.extend(flt)

            return filters
        else:
            is_hybrid_property = isinstance(attr, ColumnElement)
            if is_hybrid_property:
                column = attr
            else:
                columns = tools.get_columns_for_field(attr)

                if len(columns) > 1:
                    raise Exception(
                        'Can not filter more than on one column for %s' % name)

                column = columns[0]

            # Join not needed for hybrid properties
            if (not is_hybrid_property
                    and tools.need_join(self.model, column.table)
                    and name not in self.column_labels):
                visible_name = '%s / %s' % (self.get_column_name(
                    column.table.name), self.get_column_name(column.name))
            else:
                if not isinstance(name, string_types):
                    visible_name = self.get_column_name(name.property.key)
                else:
                    visible_name = self.get_column_name(name)

            type_name = type(column.type).__name__

            flt = self.filter_converter.convert(
                type_name,
                column,
                visible_name,
                options=self.column_choices.get(name),
            )

            if joins:
                self._filter_joins[column] = joins
            elif not is_hybrid_property and tools.need_join(
                    self.model, column.table):
                self._filter_joins[column] = [column.table]

            return flt
Example #18
0
    def scaffold_filters(self, name):
        """
            Return list of enabled filters
        """

        attr, joins = tools.get_field_with_path(self.model, name)

        if attr is None:
            raise Exception('Failed to find field for filter: %s' % name)

        # Figure out filters for related column, unless it's a hybrid_property
        if isinstance(attr, ColumnElement):
            warnings.warn(('Unable to scaffold the filter for %s, scaffolding '
                           'for hybrid_property is not supported yet.') % name)
        elif hasattr(attr, 'property') and hasattr(attr.property, 'direction'):
            filters = []

            for p in self._get_model_iterator(attr.property.mapper.class_):
                if hasattr(p, 'columns'):
                    # TODO: Check for multiple columns
                    column = p.columns[0]

                    if column.foreign_keys or column.primary_key:
                        continue

                    visible_name = '%s / %s' % (self.get_column_name(attr.prop.table.name),
                                                self.get_column_name(p.key))

                    type_name = type(column.type).__name__
                    flt = self.filter_converter.convert(type_name,
                                                        column,
                                                        visible_name)

                    if flt:
                        table = column.table

                        if joins:
                            self._filter_joins[column] = joins
                        elif tools.need_join(self.model, table):
                            self._filter_joins[column] = [table]

                        filters.extend(flt)

            return filters
        else:
            columns = tools.get_columns_for_field(attr)

            if len(columns) > 1:
                raise Exception('Can not filter more than on one column for %s' % name)

            column = columns[0]

            if (tools.need_join(self.model, column.table) and
                    name not in self.column_labels):
                visible_name = '%s / %s' % (
                    self.get_column_name(column.table.name),
                    self.get_column_name(column.name)
                )
            else:
                if not isinstance(name, string_types):
                    visible_name = self.get_column_name(name.property.key)
                else:
                    visible_name = self.get_column_name(name)

            type_name = type(column.type).__name__

            flt = self.filter_converter.convert(
                type_name,
                column,
                visible_name,
                options=self.column_choices.get(name),
            )

            if joins:
                self._filter_joins[column] = joins
            elif tools.need_join(self.model, column.table):
                self._filter_joins[column] = [column.table]

            return flt
Example #19
0
    def scaffold_filters(self, name):
        """
            Return list of enabled filters
        """

        attr, joins = tools.get_field_with_path(self.model, name)

        if attr is None:
            raise Exception("Failed to find field for filter: %s" % name)

        # Figure out filters for related column
        if is_relationship(attr):
            filters = []

            for p in self._get_model_iterator(attr.property.mapper.class_):
                if hasattr(p, "columns"):
                    # TODO: Check for multiple columns
                    column = p.columns[0]

                    if column.foreign_keys or column.primary_key:
                        continue

                    visible_name = "%s / %s" % (self.get_column_name(attr.prop.table.name), self.get_column_name(p.key))

                    type_name = type(column.type).__name__
                    flt = self.filter_converter.convert(type_name, column, visible_name)

                    if flt:
                        table = column.table

                        if joins:
                            self._filter_joins[column] = joins
                        elif tools.need_join(self.model, table):
                            self._filter_joins[column] = [table]

                        filters.extend(flt)

            return filters
        else:
            is_hybrid_property = tools.is_hybrid_property(self.model, name)
            if is_hybrid_property:
                column = attr
                if isinstance(name, string_types):
                    column.key = name.split(".")[-1]
            else:
                columns = tools.get_columns_for_field(attr)

                if len(columns) > 1:
                    raise Exception("Can not filter more than on one column for %s" % name)

                column = columns[0]

            # Join not needed for hybrid properties
            if not is_hybrid_property and tools.need_join(self.model, column.table) and name not in self.column_labels:
                visible_name = "%s / %s" % (self.get_column_name(column.table.name), self.get_column_name(column.name))
            else:
                if not isinstance(name, string_types):
                    visible_name = self.get_column_name(name.property.key)
                else:
                    column_name = self.get_column_name(name)

                    def prettify():
                        return column_name.replace(".", " / ")

                    if is_lazy_string(column_name):
                        visible_name = make_lazy_string(prettify)
                    else:
                        visible_name = prettify()

            type_name = type(column.type).__name__

            flt = self.filter_converter.convert(type_name, column, visible_name, options=self.column_choices.get(name))

            if joins:
                self._filter_joins[column] = joins
            elif not is_hybrid_property and tools.need_join(self.model, column.table):
                self._filter_joins[column] = [column.table]

            return flt
Example #20
0
 def _get_default_order(self):
     order = super(ModelView, self)._get_default_order()
     for field, direction in (order or []):
         attr, joins = tools.get_field_with_path(self.model, field)
         yield attr, joins, direction