Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions doc/adding_a_new_model_backend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,19 @@ Implementing filters
def validate(self, value):
return True

# You can "clean" values before they will be
# passed to the your data access layer
# You can "clean" the value before it is passed to the data
# access layer. Usually it is used to parse string values into
# a valid clean data type. For example, you can convert string
# to integer, etc.
def clean(self, value):
return value

# You can also convert value back to string representation
# for displaying in the URL.
def stringify(self, value):
return str(value)



Feel free ask questions if you have problems adding a new model backend.
Also, if you get stuck, try taking a look at the SQLAlchemy model backend and use it as a reference.
1 change: 1 addition & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ API
mod_base
mod_helpers
mod_model
mod_model_filters
mod_form
mod_form_rules
mod_form_fields
Expand Down
8 changes: 8 additions & 0 deletions doc/api/mod_model_filters.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
``flask_admin.model.filters``
==============================

.. automodule:: flask_admin.model.filters

.. autoclass:: BaseFilter
:members:
:exclude-members: get_url_argument
8 changes: 7 additions & 1 deletion doc/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,16 @@ the *column_exclude_list* parameter::

column_exclude_list = ['password', ]

To **make columns searchable**, or to use them for filtering, specify a list of column names::
To **make columns searchable**, specify a list of column names::

column_searchable_list = ['name', 'email']

To **use columns for filtering**, specify a list of filterable objects, where it could
be a string column's name, column, or :meth:`~flask_admin.model.filters.BaseFilter`::

column_filters = ['country']
column_filters = [User.country]
column_filters = [FilterLike("email", "Email")]

For a faster editing experience, enable **inline editing** in the list view::

Expand Down
45 changes: 45 additions & 0 deletions examples/pymongo_simple/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,51 @@ def index():
conn: MongoClient[Any] = MongoClient(mongo.get_connection_url())
db = conn.test

# Insert 5 users
users = [
{"name": "Alice", "email": "alice@example.com", "password": "pass123"},
{"name": "Bob", "email": "bob@example.com", "password": "pass456"},
{"name": "Charlie", "email": "charlie@example.com", "password": "pass789"},
{"name": "Diana", "email": "diana@example.com", "password": "pass321"},
{"name": "Eve", "email": "eve@example.com", "password": "pass654"},
]
user_ids = db.user.insert_many(users).inserted_ids

# Insert some tweets
tweets = [
{
"name": "tweet1",
"user_id": user_ids[0],
"text": "Hello World!",
"testie": True,
},
{
"name": "tweet2",
"user_id": user_ids[1],
"text": "Flask is awesome",
"testie": False,
},
{
"name": "tweet3",
"user_id": user_ids[0],
"text": "Learning MongoDB",
"testie": True,
},
{
"name": "tweet4",
"user_id": user_ids[2],
"text": "Python rocks!",
"testie": False,
},
{
"name": "tweet5",
"user_id": user_ids[3],
"text": "Admin interface demo",
"testie": True,
},
]
db.tweet.insert_many(tweets)

admin.add_view(UserView(db.user, "User"))
admin.add_view(TweetView(db.tweet, "Tweets"))

Expand Down
31 changes: 25 additions & 6 deletions flask_admin/contrib/mongoengine/filters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import typing as t

from bson.errors import InvalidId
from bson.objectid import ObjectId
from mongoengine.queryset import Q
Expand All @@ -13,7 +15,7 @@ class BaseMongoEngineFilter(filters.BaseFilter):
Base MongoEngine filter.
"""

def __init__(self, column: str, name, options=None, data_type=None):
def __init__(self, column: str, name, options=None, data_type=None, url_value=None):
"""
Constructor.

Expand All @@ -25,8 +27,10 @@ def __init__(self, column: str, name, options=None, data_type=None):
Fixed set of options. If provided, will use drop down instead of textbox.
:param data_type:
Client data type
:param url_value:
URL value
"""
super().__init__(name, options, data_type)
super().__init__(column, name, options, data_type, url_value=url_value)

self.column = column

Expand Down Expand Up @@ -101,8 +105,10 @@ def operation(self):


class FilterInList(BaseMongoEngineFilter):
def __init__(self, column, name, options=None, data_type=None):
super().__init__(column, name, options, data_type="select2-tags")
def __init__(self, column, name, options=None, data_type=None, url_value=None):
super().__init__(
column, name, options, data_type="select2-tags", url_value=url_value
)

def clean(self, value):
return [v.strip() for v in value.split(",") if v.strip()]
Expand All @@ -114,15 +120,26 @@ def apply(self, query, value):
def operation(self):
return lazy_gettext("in list")

def stringify(self, value: t.Any) -> str:
return ",".join(str(v) for v in value)


class FilterNotInList(FilterInList):
def __init__(self, column, name, options=None, data_type=None, url_value=None):
super().__init__(
column, name, options, data_type="select2-tags", url_value=url_value
)

def apply(self, query, value):
flt = {f"{self.column}__nin": value}
return query.filter(**flt)

def operation(self):
return lazy_gettext("not in list")

def stringify(self, value: t.Any) -> str:
return ",".join(str(v) for v in value)


# Customized type filters
class BooleanEqualFilter(FilterEqual, filters.BaseBooleanFilter):
Expand Down Expand Up @@ -202,8 +219,10 @@ class DateTimeSmallerFilter(FilterSmaller, filters.BaseDateTimeFilter):


class DateTimeBetweenFilter(BaseMongoEngineFilter, filters.BaseDateTimeBetweenFilter):
def __init__(self, column, name, options=None, data_type=None):
super().__init__(column, name, options, data_type="datetimerangepicker")
def __init__(self, column, name, options=None, data_type=None, url_value=None):
super().__init__(
column, name, options, data_type="datetimerangepicker", url_value=url_value
)

def apply(self, query, value):
start, end = value
Expand Down
31 changes: 26 additions & 5 deletions flask_admin/contrib/peewee/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
):
"""
Constructor.
Expand All @@ -32,8 +33,10 @@ def __init__(
Fixed set of options
:param data_type:
Client data type
:param url_value:
URL value
"""
super().__init__(name, options, data_type)
super().__init__(column, name, options, data_type, url_value=url_value)

self.column = column

Expand Down Expand Up @@ -107,8 +110,11 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
) -> None:
super().__init__(column, name, options, data_type="select2-tags")
super().__init__(
column, name, options, data_type="select2-tags", url_value=url_value
)

def clean(self, value: str) -> list[str]:
return [v.strip() for v in value.split(",") if v.strip()]
Expand All @@ -119,6 +125,9 @@ def apply(self, query: t.Any, value: t.Any) -> t.Any:
def operation(self) -> T_TRANSLATABLE:
return lazy_gettext("in list")

def stringify(self, value: t.Any) -> str:
return ",".join(str(v) for v in value)


class FilterNotInList(FilterInList):
def apply(self, query: t.Any, value: t.Any) -> t.Any:
Expand All @@ -128,6 +137,9 @@ def apply(self, query: t.Any, value: t.Any) -> t.Any:
def operation(self) -> T_TRANSLATABLE:
return lazy_gettext("not in list")

def stringify(self, value: t.Any) -> str:
return ",".join(str(v) for v in value)


# Customized type filters
class BooleanEqualFilter(FilterEqual, filters.BaseBooleanFilter):
Expand Down Expand Up @@ -211,8 +223,11 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
) -> None:
super().__init__(column, name, options, data_type="daterangepicker")
super().__init__(
column, name, options, data_type="daterangepicker", url_value=url_value
)

def apply(self, query: t.Any, value: t.Any) -> t.Any:
start, end = value
Expand Down Expand Up @@ -251,8 +266,11 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
):
super().__init__(column, name, options, data_type="datetimerangepicker")
super().__init__(
column, name, options, data_type="datetimerangepicker", url_value=url_value
)

def apply(self, query: t.Any, value: t.Any) -> t.Any:
start, end = value
Expand Down Expand Up @@ -291,8 +309,11 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
):
super().__init__(column, name, options, data_type="timerangepicker")
super().__init__(
column, name, options, data_type="timerangepicker", url_value=url_value
)

def apply(self, query: t.Any, value: t.Any) -> t.Any:
start, end = value
Expand Down
2 changes: 1 addition & 1 deletion flask_admin/contrib/peewee/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def get_list( # type: ignore[override]
for flt, _flt_name, value in filters: # type: ignore[union-attr]
f = self._filters[flt]

query = self._handle_join(query, f.column, joins) # type: ignore[attr-defined]
query = self._handle_join(query, f.column, joins)
query = f.apply(query, f.clean(value))

# Get count
Expand Down
3 changes: 2 additions & 1 deletion flask_admin/contrib/pymongo/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def __init__(
name: str,
options: T_OPTIONS = None,
data_type: T_WIDGET_TYPE = None,
url_value: t.Any = None,
) -> None:
"""
Constructor.
Expand All @@ -34,7 +35,7 @@ def __init__(
:param data_type:
Client data type
"""
super().__init__(name, options, data_type)
super().__init__(column, name, options, data_type, url_value=url_value)

self.column = column

Expand Down
Loading
Loading