Neoteroi / BlackSheep

Fast ASGI web framework for Python
https://www.neoteroi.dev/blacksheep/
MIT License
1.8k stars 75 forks source link

Request is undefined in jinja template #384

Closed Kokoserver closed 1 year ago

Kokoserver commented 1 year ago

i have this function to implement flash message def flash(request: Request, message: typing.Any, category: str = "primary") -> None: if "_messages" not in request.session: request.session["_messages"] = [] request.session["_messages"].append({"message": message, "category": category})

def get_flashed_messages(request: Request): print(request.session) return request.session.pop("_messages") if "_messages" in request.session else []

in which the flass function can be used to set the message, through session and then i can set it like the following

@post() async def test(self, request: Request): flash(request, "Failed to login", "danger") return self.view()

and i try to use in the template like this {% for message in get_flashed_messages(request) %}

{{ message.message }}

{% endfor %}

i should add that i use the mvc template and i already play with another method that doesn't use argument is works well.

i'm i the one access the request object wrong or there is a way to access session or request object differently.

RobertoPrevato commented 1 year ago

Hi @Kokoserver indeed, BlackSheep does not bind automatically the request object to HTML views. I might change mind in the future, but I preferred to do it this way by default and I still think it's a good default behavior.

For runtime values that must be always available to Jinja templates, I would define a custom Controller class that does that:

class MyController(Controller):
    async def on_request(self, request: Request):
        self._request = request

    async def on_response(self, response: Response):
        self._request = None

    def view(
        self, name: str | None = None, model: Any | None = None, **kwargs
    ) -> Response:
        kwargs.update(dict(request=self._request))  # <-- add common properties for Jinja views
        return super().view(name, model, **kwargs)

Then I would use that class for my controllers:

class Home(MyController):
    @get()
    def index(self, request: Request):
        flash(request, "Failed to login", "danger")
        return self.view()
RobertoPrevato commented 1 year ago

Also, please note the right way to implement your flash method is as follows:

def flash(request, message, category: str = "primary") -> None:
    messages = request.session.get("_messages", [])
    messages.append({"message": message, "category": category})
    request.session["_messages"] = messages

Because if you modify the _messages property appending new messages, the Session object itself is not marked as modified, and the session does not get updated. This might be a surprising behavior, but it is an optimization, so that the session cookie is set only if the session object is modified using its write methods. I now realize I need to document better this detail.

Kokoserver commented 1 year ago

Thanks for quick response. 🙏