update filters

This commit is contained in:
long2ice
2021-05-08 22:58:04 +08:00
parent 300dd4bd06
commit 3e9c6f636e
9 changed files with 75 additions and 71 deletions

View File

@@ -2,6 +2,7 @@ from typing import Dict, List, Optional, Type
from aioredis import Redis
from fastapi import FastAPI
from pydantic import HttpUrl
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_500_INTERNAL_SERVER_ERROR
from tortoise import Model
@@ -29,6 +30,7 @@ class FastAPIAdmin(FastAPI):
model_resources: Dict[Type[Model], Type[Resource]] = {}
redis: Redis
language_switch: bool = True
favicon_url: Optional[HttpUrl] = None
async def configure(
self,
@@ -39,12 +41,14 @@ class FastAPIAdmin(FastAPI):
admin_path: str = "/admin",
template_folders: Optional[List[str]] = None,
providers: Optional[List[Provider]] = None,
favicon_url: Optional[HttpUrl] = None,
):
self.redis = redis
i18n.set_locale(default_locale)
self.admin_path = admin_path
self.language_switch = language_switch
self.logo_url = logo_url
self.favicon_url = favicon_url
if template_folders:
template.add_template_folder(*template_folders)
await self._register_providers(providers)

View File

@@ -7,6 +7,7 @@ from tortoise import ForeignKeyFieldInstance, ManyToManyFieldInstance
from tortoise import Model as TortoiseModel
from tortoise.fields import BooleanField, DateField, DatetimeField, JSONField
from tortoise.fields.data import CharEnumFieldInstance, IntEnumFieldInstance, IntField, TextField
from tortoise.queryset import QuerySet
from fastapi_admin.exceptions import NoSuchFieldFound
from fastapi_admin.i18n import _
@@ -107,14 +108,15 @@ class Model(Resource):
return ret
@classmethod
async def resolve_query_params(cls, request: Request, values: dict):
async def resolve_query_params(cls, request: Request, values: dict, qs: QuerySet):
ret = {}
for f in cls.filters:
name = f.context.get("name")
v = values.get(name)
if v is not None and v != "":
ret[name] = await f.parse_value(request, v)
return ret
qs = await f.get_queryset(request, v, qs)
return ret, qs
@classmethod
async def resolve_data(cls, request: Request, data: FormData):

View File

@@ -28,9 +28,9 @@ async def list_view(
fields_name = model_resource.get_fields_name()
fields_label = model_resource.get_fields_label()
fields = model_resource.get_fields()
params = await model_resource.resolve_query_params(request, dict(request.query_params))
qs = model.all()
params, qs = await model_resource.resolve_query_params(request, dict(request.query_params), qs)
filters = await model_resource.get_filters(request, params)
qs = model.filter(**params)
total = await qs.count()
if page_size:
qs = qs.limit(page_size)

View File

@@ -9,6 +9,7 @@
<link rel="stylesheet" href="https://unpkg.com/@tabler/core@latest/dist/css/tabler-vendors.min.css">
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons@latest/iconfont/tabler-icons.min.css">
<script src="https://kit.fontawesome.com/65694932fa.js" crossorigin="anonymous"></script>
<link rel="icon" href="{{ request.app.favicon_url }}">
<script src="https://cdn.jsdelivr.net/npm/choices.js/public/assets/scripts/choices.js"></script>
<script src="https://unpkg.com/@tabler/core@latest/dist/js/tabler.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>

View File

@@ -3,35 +3,21 @@
<div class="text-muted">
{{ label }}:
<div class="d-inline-block">
<input class="form-control" type="text" id="{{ name }}" name="{{ name }}" value="{{ value }}">
<input {% if not null %}
required
{% endif %} class="form-control" type="text" id="{{ name }}" name="{{ name }}" value="{{ value }}">
</div>
</div>
<script>
$(function () {
let value = "{{value}}";
let start, end;
if (value !== '') {
let s = value.split(' - ')
start = moment(s[0]);
end = moment(s[1]);
} else {
start = moment().subtract(7, 'days');
end = moment().subtract(-1, 'days');
}
let format = '{{ format }}';
function cb(start, end) {
$('#{{name}} span').html(start.format(format) + ' - ' + end.format(format));
}
$('#{{name}}').daterangepicker({
startDate: start,
endDate: end,
let option = {
autoUpdateInput: false,
timePicker: true,
timePicker24Hour: true,
locale: {
format: format
format: format,
},
ranges: {
'Today': [moment(), moment()],
@@ -41,7 +27,26 @@
'This Month': [moment().startOf('month'), moment().endOf('month')],
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
}
}, cb);
cb(start, end);
};
if (value !== '') {
let s = value.split(' - ')
option.startDate = moment(s[0]);
option.endDate = moment(s[1]);
}
function cb(start, end) {
$('#{{name}} span').html(start.format(format) + ' - ' + end.format(format));
}
let element = $('#{{name}}');
element.daterangepicker(option, cb);
element.on('apply.daterangepicker', function (ev, picker) {
$(this).val(picker.startDate.format(format) + ' - ' + picker.endDate.format(format));
});
element.on('cancel.daterangepicker', function (ev, picker) {
$(this).val('');
});
});
</script>

View File

@@ -4,19 +4,25 @@ from typing import Any, Optional, Type
from starlette.requests import Request
from tortoise import Model
from tortoise.queryset import QuerySet
from fastapi_admin import constants
from fastapi_admin.widgets.inputs import Input
class Filter(Input):
def __init__(self, name: str, label: str, placeholder: str = ""):
def __init__(self, name: str, label: str, placeholder: str = "", null: bool = True, **context):
"""
Parent class for all filters
:param name: model field name
:param label:
"""
super().__init__(name=name, label=label, placeholder=placeholder)
super().__init__(name=name, label=label, placeholder=placeholder, null=null, **context)
async def get_queryset(self, request: Request, value: Any, qs: QuerySet):
value = await self.parse_value(request, value)
filters = {self.context.get("name"): value}
return qs.filter(**filters)
class Search(Filter):
@@ -28,6 +34,7 @@ class Search(Filter):
label: str,
search_mode: str = "equal",
placeholder: str = "",
null: bool = True,
):
"""
Search for keyword
@@ -36,7 +43,7 @@ class Search(Filter):
:param search_mode: equal,contains,icontains,startswith,istartswith,endswith,iendswith,iexact,search
"""
if search_mode == "equal":
super().__init__(name, label, placeholder)
super().__init__(name, label, placeholder, null)
else:
super().__init__(name + "__" + search_mode, label, placeholder)
self.context.update(search_mode=search_mode)
@@ -46,10 +53,7 @@ class Datetime(Filter):
template = "widgets/filters/datetime.html"
def __init__(
self,
name: str,
label: str,
format_: str = constants.DATETIME_FORMAT_MOMENT,
self, name: str, label: str, format_: str = constants.DATE_FORMAT_MOMENT, null: bool = True
):
"""
Datetime filter
@@ -57,15 +61,10 @@ class Datetime(Filter):
:param label:
:param format_: the format of moment.js
"""
super().__init__(
name + "__range",
label,
)
self.context.update(format=format_)
super().__init__(name + "__range", label, null=null, format=format_)
async def parse_value(self, request: Request, value: Optional[str]):
if value:
return value.split(" - ")
return value.split(" - ")
async def render(self, request: Request, value: Any):
if value is not None:
@@ -75,24 +74,16 @@ class Datetime(Filter):
class Date(Datetime):
def __init__(
self,
name: str,
label: str,
format_: str = constants.DATE_FORMAT_MOMENT,
self, name: str, label: str, format_: str = constants.DATE_FORMAT_MOMENT, null: bool = True
):
super().__init__(
name,
label,
format_,
)
super().__init__(name=name, label=label, format_=format_, null=null)
class Select(Filter):
template = "widgets/filters/select.html"
def __init__(self, name: str, label: str, null: bool = True):
super().__init__(name, label)
self.null = null
super().__init__(name, label, null=null)
@abc.abstractmethod
async def get_options(self):
@@ -119,7 +110,7 @@ class Enum(Select):
enum_type: Type = int,
null: bool = True,
):
super().__init__(name, label, null)
super().__init__(name=name, label=label, null=null)
self.enum = enum
self.enum_type = enum_type
@@ -128,7 +119,7 @@ class Enum(Select):
async def get_options(self):
options = [(v.name, v.value) for v in self.enum]
if self.null:
if self.context.get("null"):
options = [("", "")] + options
return options
@@ -139,7 +130,7 @@ class ForeignKey(Select):
self.model = model
async def get_options(self):
ret = await self.get_queryset()
ret = await self.get_models()
options = [
(
str(x),
@@ -147,11 +138,11 @@ class ForeignKey(Select):
)
for x in ret
]
if self.null:
if self.context.get("null"):
options = [("", "")] + options
return options
async def get_queryset(self):
async def get_models(self):
return await self.model.all()
async def render(self, request: Request, value: Any):