Source code for url_filter.filtersets.sqlalchemy

# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import inspect
from functools import partial

from django import forms
from sqlalchemy.orm.properties import ColumnProperty, RelationshipProperty
from sqlalchemy.types import (
    BIGINT,
    CHAR,
    CLOB,
    DATE,
    DECIMAL,
    INTEGER,
    SMALLINT,
    TIMESTAMP,
    VARCHAR,
    BigInteger,
    Boolean,
    Date,
    DateTime,
    Float,
    Integer,
    Numeric,
    String,
)

from ..backends.sqlalchemy import SQLAlchemyFilterBackend
from ..exceptions import SkipFilter
from ..filters import Filter
from ..utils import SubClassDict
from .base import FilterSet
from .django import ModelFilterSetOptions


__all__ = ['SQLAlchemyModelFilterSet']


_STRING = lambda field, column: forms.CharField(max_length=column.type.length)

SQLALCHEMY_FIELD_MAPPING = SubClassDict({
    BIGINT: forms.IntegerField,
    BigInteger: forms.IntegerField,
    Integer: forms.IntegerField,
    Boolean: partial(forms.BooleanField, required=False),
    CHAR: _STRING,
    CLOB: _STRING,
    DATE: forms.DateTimeField,
    Date: forms.DateField,
    DateTime: forms.DateTimeField,
    DECIMAL: forms.DecimalField,
    Float: forms.FloatField,
    INTEGER: forms.IntegerField,
    Numeric: forms.IntegerField,
    SMALLINT: forms.IntegerField,
    String: _STRING,
    TIMESTAMP: forms.DateTimeField,
    VARCHAR: _STRING,
})


[docs]class SQLAlchemyModelFilterSet(FilterSet): """ ``FilterSet`` for SQLAlchemy models. The filterset can be configured via ``Meta`` class attribute, very much like Django's ``ModelForm`` is configured. """ filter_options_class = ModelFilterSetOptions
[docs] def get_filters(self): """ Get all filters defined in this filterset including filters corresponding to Django model fields. """ filters = super(SQLAlchemyModelFilterSet, self).get_filters() assert self.Meta.model, ( '{}.Meta.model is missing. Please specify the model ' 'in order to use ModelFilterSet.' ''.format(self.__class__.__name__) ) if self.Meta.fields is None: self.Meta.fields = self.get_model_field_names() fields = SQLAlchemyFilterBackend._get_properties_for_model(self.Meta.model) for name in self.Meta.fields: if name in self.Meta.exclude: continue field = fields[name] try: _filter = None if isinstance(field, ColumnProperty): _filter = self.build_filter_from_field(field) elif isinstance(field, RelationshipProperty): if not self.Meta.allow_related: raise SkipFilter _filter = self.build_filterset_from_related_field(field) except SkipFilter: continue else: if _filter is not None: filters[name] = _filter return filters
[docs] def get_model_field_names(self): """ Get a list of all model fields. This is used when ``Meta.fields`` is ``None`` in which case this method returns all model fields. """ return list(SQLAlchemyFilterBackend._get_properties_for_model(self.Meta.model).keys())
[docs] def get_form_field_for_field(self, field): """ Get form field for the given SQLAlchemy model field. """ column = SQLAlchemyFilterBackend._get_column_for_field(field) form_field = SQLALCHEMY_FIELD_MAPPING.get( column.type.__class__, None, ) if form_field is None: raise SkipFilter if inspect.isclass(form_field) or isinstance(form_field, partial): return form_field() else: return form_field(field, column)
[docs] def build_filter_from_field(self, field): """ Build ``Filter`` for a standard SQLAlchemy model field. """ column = SQLAlchemyFilterBackend._get_column_for_field(field) return Filter( form_field=self.get_form_field_for_field(field), is_default=column.primary_key, )