aiohttp-session icon indicating copy to clipboard operation
aiohttp-session copied to clipboard

Flash messages support

Open jettify opened this issue 10 years ago • 9 comments

Do we need some sort of flash messages support? Or may be this feature is out of scope of aiohttp_session?

Most frameworks have flash messages: pyramid: http://docs.pylonsproject.org/projects/pyramid/en/1.0-branch/narr/sessions.html#flash-messages flask: http://flask.pocoo.org/docs/0.10/patterns/flashing/

Particularly pyramid has it integrated to session object.

jettify avatar Jan 03 '16 20:01 jettify

I think it will look better as a separate package because it brings some dependencies like aiohttp_jinja2 for context_processor implementation.

imbolc avatar Jan 04 '16 03:01 imbolc

Do not follow, it is couple of methods: flash() - add message to the queue, where queue is located in session object pop_flash() - pop messages from the queue

Do not see why it should depend on aiohttp_jinja2

jettify avatar Jan 04 '16 10:01 jettify

Because flash messages doesn't make much sense without templates. And I think there is not so much code to split it into parts. Exactly it took 22 loc in my last implementation: https://gist.github.com/imbolc/ca43545ca31329b4f9e2

imbolc avatar Jan 04 '16 12:01 imbolc

Also, this approach has benefits of brevity:

flash(request, ('error', 'Foo error'))

instead of

session = await get_session(request)
session.flash(request, ('error', 'Foo error'))

imbolc avatar Jan 04 '16 12:01 imbolc

@imbolc request is not necessary in your last example. session is coupled to request itself. Anyway, let's postpone the issue. For now I have a feeling that flash messages are part of UI library (while they are built on top of user session, sure).

Another interesting question is API for CSRF checks BTW.

asvetlov avatar Jan 04 '16 12:01 asvetlov

I also do not understand how flash depends on template engine. As @asvetlov mentioned, flash is necessary to implement CSRF check.

shicky avatar May 13 '16 02:05 shicky

I created the simple session flash library based on @imbolc's gist:

https://github.com/IlyaSemenov/aiohttp_session_flash

It has a few enhancements over the original gist:

  • session is not re-saved on each HTTP request unless flash messages were pushed or popped
  • it doesn't lose flash messages followed by raise web.HTTPFound(...)
  • it has tests and pypi package

IlyaSemenov avatar Jun 21 '16 17:06 IlyaSemenov

I did not know about the flash example and I implemented my own thing. I didn't use middleware though. I did this:

class Session:
    def __init__(self, session):
        self.session = session

    async def get(self, getter):
        return self.session[getter] if getter in self.session else None

    async def set(self, setter, content):
        self.session[setter] = content

Is the middleware just so that we can access via request? Would be great to have the flash example commented.

bitnom avatar Mar 04 '20 07:03 bitnom

Here is my super flash function: (I used with jinja2)

flash.py

from functools import partial
from aiohttp_session import Session

def message(session: Session, message: str, level: str = "info") -> None:
    session.setdefault("flash", []).append((message, level))

debug = partial(message, level="debug")
info = partial(message, level="info")
success = partial(message, level="success")
warning = partial(message, level="warning")
error = partial(message, level="danger")

app.py

import flash
from aiohttp import web
from aiohttp_jinja2 import setup as set_jinja
from aiohttp_session import get_session, setup as set_session
from jinja2 import FileSystemLoader

@template('index.html')
async def index(rq: web.Request):
    session = await get_session(rq)
    flash.error(session, 'Hello world!')
    ...

async def jinja_ctx_processor(rq: web.Request):
    session = await get_session(rq)
    return {"get_flashed_messages": lambda: session.pop("flash", [])}

app = web.Application()
app.add_routes([web.get('/', index)])
set_session(app, EncryptedCookieStorage('secret_key', cookie_name='session_app'))
set_jinja(app, loader=FileSystemLoader('templates_path'), context_processors=[jinja_ctx_processor])
web.run_app(app)

index.html

<html>
  {% for message, level in get_flashed_messages() %}
    <div class="alert alert-{{ level }} alert-dismissible fade show" role="alert">
      {{ message }}
      <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
    </div>
  {% endfor %}
</html>

wagnerc4 avatar Jun 16 '22 21:06 wagnerc4