AnswerDotAI / fasthtml

The fastest way to create an HTML app
https://fastht.ml/
Apache License 2.0
5.08k stars 206 forks source link

[BUG] function name that is decorated by route controls if route can be matched or not rather than the route #442

Open rbavery opened 3 hours ago

rbavery commented 3 hours ago

Describe the bug

I get an error starlette.routing.NoMatchFound: No route exists for name "get_lesson" and params "". if the function name that is decorated by @app.route does not match the get= argument in a component.

for example, in the below cascading dropdowns example I want to show a different dropdown based on the selection in an initial dropdown. The function name get_lesson_bug, is incompatible with get=get_lesson and causes the NoMatchFound error. If I change the function name to get_lesson it works, but this is unexpected behavior.

Minimal Reproducible Example

from fasthtml.common import *

app, rt = fast_app()

chapters = ['ch1', 'ch2', 'ch3']
lessons = {
    'ch1': ['lesson1', 'lesson2', 'lesson3'],
    'ch2': ['lesson4', 'lesson5', 'lesson6'],
}

def mk_opts(nm, cs):
    return (
        Option(f'-- select {nm} --', disabled='', selected='', value=''),
        *map(Option, cs))

@app.get('/get_lesson')
def get_lesson_bug(chapter: str):
    return Select(*mk_opts('lesson', lessons[chapter]), name='lesson')

@app.get('/')
def homepage():
    chapter_dropdown = Select(
        *mk_opts('chapter', chapters),
        name='chapter',
        get='get_lesson', hx_target='#lesson')

    return Div(
        Div(
            Label("Chapter:", for_="chapter"),
            chapter_dropdown),
        Div(
            Label("Lesson:", for_="lesson"),
            Div(Div(id='lesson')),
    ))

serve()

removing _bug allows the route to be found

Expected behavior

If I use the function name get_lesson_bug, with @app.get('/get_lesson') I expect the argument get=get_lesson to use the value supplied to @app.get not the function name. similar to how values are passed to hx_post that refer to routes.

Environment Information Please provide the following version information:

Confirmation Please confirm the following:

Additional context

the docs for @app.get() say

In the previous example, the function name (getnm) didn’t actually matter – we could have just called it , for instance, since we never actually call it directly. It’s just called through HTTP. In fact, we often do call our functions _ when using this style of route, since that’s one less thing we have to worry about, naming.

so I think the issue above illustrates there is a conflict with the docs https://docs.fastht.ml/explains/routes.html

jph00 commented 3 hours ago

@pydanny you want to look at this one? (@Isaac-Flath could possibly help point you in the right direction)

Isaac-Flath commented 2 hours ago

@pydanny @jph00 Based on my understanding, I don't think this is a bug, but here are my thoughts and some background. Let me know if I am misunderstanding something.

get and post take route names as the argument. The default name of a route is the function name, but you can name it something different via @app.get('/get_lesson',name='myCustomName'). However, in the example provided in this issue the name of the route is get_lesson_bug because the default route name is the python function name (no name was passed to @app.get)

hx_get and hx_post take routes as arguments so that you can specify routes. You could do hx_get='/get_lesson', which would route it to the /get_lesson route. You could also do hx_get='get_lesson', which would route it to the same place unless you're doing something special with mounting (it's like a relative path without the leading /). But in both these cases, it refers to the route path and not the route's name.

I think using the function name as the route name is a good default. Otherwise, we need to transform the route to a valid python name, such as replacing slashes with underscores or something and removing {params}. I think it's clearer to have the default route name be the python function name.