From 9e50bc9f02828d1f22c04c63a9c629038c765f86 Mon Sep 17 00:00:00 2001 From: Peter Stein Date: Sun, 1 Sep 2024 00:11:32 +0200 Subject: [PATCH] Fix Deprecation Warnings for TemplateResponse and Jinja2Templates (#575) * Ignore .venv * Fix DeprecationWarning: The `name` is not the first parameter anymore. Addresses /~https://github.com/jowilf/starlette-admin/issues/574 DeprecationWarning: The `name` is not the first parameter anymore. The first parameter should be the `Request` instance. Replace `TemplateResponse(name, {"request": request})` by `TemplateResponse(request, name)`. warnings.warn( Before Starlette 0.29.0, the name was the first parameter. Also, before that, in previous versions, the request object was passed as part of the key-value pairs in the context for Jinja2. See: - https://www.starlette.io/release-notes/#0290 - /~https://github.com/encode/starlette/pull/2191 - /~https://github.com/encode/starlette/blob/c78c9aac17a4d68e0647252310044502f1b7da71/starlette/templating.py#L166-L178 - https://fastapi.tiangolo.com/reference/templating/#fastapi.templating.Jinja2Templates.TemplateResponse - https://fastapi.tiangolo.com/advanced/templates/#using-jinja2templates * Fix DeprecationWarning: Extra environment options are deprecated. Resolves /~https://github.com/jowilf/starlette-admin/issues/574 According to the Starlette 0.28.0 release notes, the **env_options parameter in Jinja2Templates has been deprecated in favor of the new env parameter. The relevant pull request #2159 explains this change. See: - /~https://github.com/encode/starlette/pull/2159 - https://www.starlette.io/release-notes/#0280 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .gitignore | 1 + docs/user-guide/getting-started/index.md | 4 +- starlette_admin/auth.py | 15 +++--- starlette_admin/base.py | 60 +++++++++++++++--------- starlette_admin/views.py | 4 +- 5 files changed, 53 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index 9cecd06a..a9477233 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ env* .mypy_cache .vscode .idea +.venv poetry.lock dist htmlcov diff --git a/docs/user-guide/getting-started/index.md b/docs/user-guide/getting-started/index.md index 63f58ad0..06959e02 100644 --- a/docs/user-guide/getting-started/index.md +++ b/docs/user-guide/getting-started/index.md @@ -132,7 +132,9 @@ from starlette_admin import CustomView class HomeView(CustomView): async def render(self, request: Request, templates: Jinja2Templates) -> Response: return templates.TemplateResponse( - "home.html", {"request": request, "latest_posts": ..., "top_users": ...} + request, + name="home.html", + context={"latest_posts": ..., "top_users": ...}, ) diff --git a/starlette_admin/auth.py b/starlette_admin/auth.py index 0a0f9396..cb824de0 100644 --- a/starlette_admin/auth.py +++ b/starlette_admin/auth.py @@ -233,8 +233,9 @@ async def render_login(self, request: Request, admin: "BaseAdmin") -> Response: """Render the default login page for username & password authentication.""" if request.method == "GET": return admin.templates.TemplateResponse( - "login.html", - {"request": request, "_is_login_path": True}, + request=request, + name="login.html", + context={"_is_login_path": True}, ) form = await request.form() try: @@ -251,14 +252,16 @@ async def render_login(self, request: Request, admin: "BaseAdmin") -> Response: ) except FormValidationError as errors: return admin.templates.TemplateResponse( - "login.html", - {"request": request, "form_errors": errors, "_is_login_path": True}, + request=request, + name="login.html", + context={"form_errors": errors, "_is_login_path": True}, status_code=HTTP_422_UNPROCESSABLE_ENTITY, ) except LoginFailed as error: return admin.templates.TemplateResponse( - "login.html", - {"request": request, "error": error.msg, "_is_login_path": True}, + request=request, + name="login.html", + context={"error": error.msg, "_is_login_path": True}, status_code=HTTP_400_BAD_REQUEST, ) diff --git a/starlette_admin/base.py b/starlette_admin/base.py index cf0ffc26..fccf074c 100644 --- a/starlette_admin/base.py +++ b/starlette_admin/base.py @@ -2,7 +2,7 @@ from json import JSONDecodeError from typing import Any, Awaitable, Callable, Dict, List, Optional, Sequence, Type, Union -from jinja2 import ChoiceLoader, FileSystemLoader, PackageLoader +from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader from starlette.applications import Starlette from starlette.datastructures import FormData from starlette.exceptions import HTTPException @@ -190,13 +190,17 @@ def init_routes(self) -> None: self._views.append(self.index_view) def _setup_templates(self) -> None: - templates = Jinja2Templates(self.templates_dir, extensions=["jinja2.ext.i18n"]) - templates.env.loader = ChoiceLoader( - [ - FileSystemLoader(self.templates_dir), - PackageLoader("starlette_admin", "templates"), - ] + env = Environment( + loader=ChoiceLoader( + [ + FileSystemLoader(self.templates_dir), + PackageLoader("starlette_admin", "templates"), + ] + ), + extensions=["jinja2.ext.i18n"], ) + templates = Jinja2Templates(env=env) + # globals templates.env.globals["views"] = self._views templates.env.globals["app_title"] = self.title @@ -374,9 +378,9 @@ async def _render_list(self, request: Request) -> Response: if not model.is_accessible(request): raise HTTPException(HTTP_403_FORBIDDEN) return self.templates.TemplateResponse( - model.list_template, - { - "request": request, + request=request, + name=model.list_template, + context={ "model": model, "title": model.title(request), "_actions": await model.get_all_actions(request), @@ -395,9 +399,9 @@ async def _render_detail(self, request: Request) -> Response: if obj is None: raise HTTPException(HTTP_404_NOT_FOUND) return self.templates.TemplateResponse( - model.detail_template, - { - "request": request, + request=request, + name=model.detail_template, + context={ "title": model.title(request), "model": model, "raw_obj": obj, @@ -410,11 +414,15 @@ async def _render_create(self, request: Request) -> Response: request.state.action = RequestAction.CREATE identity = request.path_params.get("identity") model = self._find_model_from_identity(identity) - config = {"request": request, "title": model.title(request), "model": model} + config = {"title": model.title(request), "model": model} if not model.is_accessible(request) or not model.can_create(request): raise HTTPException(HTTP_403_FORBIDDEN) if request.method == "GET": - return self.templates.TemplateResponse(model.create_template, config) + return self.templates.TemplateResponse( + request=request, + name=model.create_template, + context=config, + ) form = await request.form() dict_obj = await self.form_to_dict(request, form, model, RequestAction.CREATE) try: @@ -427,8 +435,9 @@ async def _render_create(self, request: Request) -> Response: } ) return self.templates.TemplateResponse( - model.create_template, - config, + request=request, + name=model.create_template, + context=config, status_code=HTTP_422_UNPROCESSABLE_ENTITY, ) pk = await model.get_pk_value(request, obj) @@ -452,14 +461,17 @@ async def _render_edit(self, request: Request) -> Response: if obj is None: raise HTTPException(HTTP_404_NOT_FOUND) config = { - "request": request, "title": model.title(request), "model": model, "raw_obj": obj, "obj": await model.serialize(obj, request, RequestAction.EDIT), } if request.method == "GET": - return self.templates.TemplateResponse(model.edit_template, config) + return self.templates.TemplateResponse( + request=request, + name=model.edit_template, + context=config, + ) form = await request.form() dict_obj = await self.form_to_dict(request, form, model, RequestAction.EDIT) try: @@ -472,8 +484,9 @@ async def _render_edit(self, request: Request) -> Response: } ) return self.templates.TemplateResponse( - model.edit_template, - config, + request=request, + name=model.edit_template, + context=config, status_code=HTTP_422_UNPROCESSABLE_ENTITY, ) pk = await model.get_pk_value(request, obj) @@ -493,8 +506,9 @@ async def _render_error( ) -> Response: assert isinstance(exc, HTTPException) return self.templates.TemplateResponse( - "error.html", - {"request": request, "exc": exc}, + request=request, + name="error.html", + context={"exc": exc}, status_code=exc.status_code, ) diff --git a/starlette_admin/views.py b/starlette_admin/views.py index 8dd197ef..0f5ac855 100644 --- a/starlette_admin/views.py +++ b/starlette_admin/views.py @@ -165,7 +165,9 @@ def __init__( async def render(self, request: Request, templates: Jinja2Templates) -> Response: """Default methods to render view. Override this methods to add your custom logic.""" return templates.TemplateResponse( - self.template_path, {"request": request, "title": self.title(request)} + request=request, + name=self.template_path, + context={"title": self.title(request)}, ) def is_active(self, request: Request) -> bool: