# -*- 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 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,
)