diff --git a/docs/en/docs/getting_started/quick_start.md b/docs/en/docs/getting_started/quick_start.md index 16e37e8..4956f36 100644 --- a/docs/en/docs/getting_started/quick_start.md +++ b/docs/en/docs/getting_started/quick_start.md @@ -38,8 +38,7 @@ async def startup(): logo_url="https://preview.tabler.io/static/logo-white.svg", login_logo_url="https://preview.tabler.io/static/logo.svg", template_folders=[os.path.join(BASE_DIR, "templates")], - login_provider=login_provider, - maintenance=False, + providers=[login_provider], redis=redis, ) ``` @@ -119,10 +118,10 @@ class AdminResource(Model): ] can_create = False - def get_actions(self) -> List[Action]: + async def get_actions(self,request:Request) -> List[Action]: return [] - def get_bulk_actions(self) -> List[Action]: + async def get_bulk_actions(self,request:Request) -> List[Action]: return [] ``` diff --git a/examples/resources.py b/examples/resources.py index 30d7d43..8b417dd 100644 --- a/examples/resources.py +++ b/examples/resources.py @@ -1,6 +1,8 @@ import os from typing import List +from starlette.requests import Request + from examples import enums from examples.constants import BASE_DIR from examples.models import Admin, Category, Config, Product @@ -55,10 +57,10 @@ class AdminResource(Model): ] can_create = False - def cell_attributes(self, obj: dict, field: Field) -> dict: + async def cell_attributes(self, request: Request, obj: dict, field: Field) -> dict: if field.name == "id": - return {"class": "bg-danger"} - return super().cell_attributes(obj, field) + return {"class": "bg-danger text-white"} + return await super().cell_attributes(request, obj, field) def get_actions(self) -> List[Action]: return [] @@ -119,10 +121,10 @@ class ConfigResource(Model): ), ] - def row_attributes(self, obj: dict) -> dict: + async def row_attributes(self, request: Request, obj: dict) -> dict: if obj.get("status") == enums.Status.on: return {"class": "bg-green text-white"} - return super().row_attributes(obj) + return await super().row_attributes(obj) @app.register diff --git a/fastapi_admin/resources.py b/fastapi_admin/resources.py index 04de351..1eef6ca 100644 --- a/fastapi_admin/resources.py +++ b/fastapi_admin/resources.py @@ -2,6 +2,7 @@ from typing import List, Optional, Type, Union from pydantic import BaseModel from starlette.datastructures import FormData +from starlette.requests import Request from tortoise import ForeignKeyFieldInstance, ManyToManyFieldInstance from tortoise import Model as TortoiseModel from tortoise.fields import BooleanField, DateField, DatetimeField, JSONField @@ -70,10 +71,10 @@ class Model(Resource): can_create: bool = True enctype = "application/x-www-form-urlencoded" - def row_attributes(self, obj: dict) -> dict: + async def row_attributes(self, request: Request, obj: dict) -> dict: return {} - def cell_attributes(self, obj: dict, field: Field) -> dict: + async def cell_attributes(self, request: Request, obj: dict, field: Field) -> dict: return {} def get_actions(self) -> List[Action]: @@ -135,14 +136,14 @@ class Model(Resource): return ret, m2m_ret @classmethod - async def get_filters(cls, values: Optional[dict] = None): + async def get_filters(cls, request: Request, values: Optional[dict] = None): if not values: values = {} ret = [] for f in cls.filters: name = f.context.get("name") value = values.get(name) - ret.append(await f.render(value)) + ret.append(await f.render(request, value)) return ret @classmethod diff --git a/fastapi_admin/routes/resources.py b/fastapi_admin/routes/resources.py index 7fcb99a..1609fd9 100644 --- a/fastapi_admin/routes/resources.py +++ b/fastapi_admin/routes/resources.py @@ -29,7 +29,7 @@ async def list_view( fields_label = model_resource.get_fields_label() fields = model_resource.get_fields() params = await model_resource.resolve_query_params(dict(request.query_params)) - filters = await model_resource.get_filters(params) + filters = await model_resource.get_filters(request, params) qs = model.filter(**params) total = await qs.count() if page_size: @@ -38,13 +38,17 @@ async def list_view( page_size = model_resource.page_size qs = qs.offset((page_num - 1) * page_size) values = await qs.values(*fields_name) - rendered_values = await render_values(fields, values) + rendered_values, row_attributes, cell_attributes = await render_values( + request, model_resource, fields, values + ) context = { "request": request, "resources": resources, "fields_label": fields_label, "fields": fields, "values": values, + "row_attributes": row_attributes, + "cell_attributes": cell_attributes, "rendered_values": rendered_values, "filters": filters, "resource": resource, diff --git a/fastapi_admin/template.py b/fastapi_admin/template.py index 095f8cf..1ff73df 100644 --- a/fastapi_admin/template.py +++ b/fastapi_admin/template.py @@ -12,7 +12,7 @@ from fastapi_admin import VERSION from fastapi_admin.constants import BASE_DIR if typing.TYPE_CHECKING: - from fastapi_admin.resources import Field + from fastapi_admin.resources import Field, Model templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "templates")) templates.env.globals["VERSION"] = VERSION @@ -46,22 +46,34 @@ def add_template_folder(*folders: str): async def render_values( - fields: List["Field"], values: List[Tuple[Any]], display: bool = True -) -> List[List[Any]]: + request: Request, + model: "Model", + fields: List["Field"], + values: List[typing.Dict[str, Any]], + display: bool = True, +) -> typing.Tuple[List[List[Any]], List[dict], List[List[dict]]]: """ render values with template render :param fields: :param values: :param display: + :params request: + :params model: :return: """ ret = [] + cell_attributes: List[List[dict]] = [] + row_attributes: List[dict] = [] for value in values: + row_attributes.append(await model.row_attributes(request, value)) item = [] + cell_item = [] for i, k in enumerate(value): + cell_item.append(await model.cell_attributes(request, value, fields[i])) if display: - item.append(await fields[i].display.render(value[k])) + item.append(await fields[i].display.render(request, value[k])) else: - item.append(await fields[i].input.render(value[k])) + item.append(await fields[i].input.render(request, value[k])) ret.append(item) - return ret + cell_attributes.append(cell_item) + return ret, row_attributes, cell_attributes diff --git a/fastapi_admin/templates/list.html b/fastapi_admin/templates/list.html index f4f309b..19404f8 100644 --- a/fastapi_admin/templates/list.html +++ b/fastapi_admin/templates/list.html @@ -108,7 +108,7 @@ {% for value in rendered_values %} - + {% if model_resource.bulk_actions %} {{ x|safe }} {% endfor %} {% endwith %} diff --git a/poetry.lock b/poetry.lock index 14baea3..2e4b7f5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -84,17 +84,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "babel" @@ -643,7 +643,7 @@ python-versions = ">=3.7,<4.0" [[package]] name = "pytest" -version = "6.2.3" +version = "6.2.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -768,7 +768,7 @@ python-versions = "*" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -974,8 +974,8 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.1.0-py2.py3-none-any.whl", hash = "sha256:8ee1e5f5a1afc5b19bdfae4fdf0c35ed324074bdce3500c939842c8f818645d9"}, + {file = "attrs-21.1.0.tar.gz", hash = "sha256:3901be1cb7c2a780f14668691474d9252c070a756be0a9ead98cfeabfa11aeb8"}, ] babel = [ {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, @@ -1365,8 +1365,8 @@ pypika-tortoise = [ {file = "pypika_tortoise-0.1.0-py3-none-any.whl", hash = "sha256:ec83b0b2964be01ef563f5f019b0332a18177604e841c47ad39d798798c6dfe9"}, ] pytest = [ - {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, - {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, + {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, ] pytest-asyncio = [ {file = "pytest-asyncio-0.15.1.tar.gz", hash = "sha256:2564ceb9612bbd560d19ca4b41347b54e7835c2f792c504f698e05395ed63f6f"}, @@ -1470,8 +1470,8 @@ regex = [ {file = "regex-2021.4.4.tar.gz", hash = "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] smmap = [ {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},