Adds integration of the Chameleon template language to FastAPI.

Overview

fastapi-chameleon

Adds integration of the Chameleon template language to FastAPI. If you are interested in Jinja instead, see the sister project: github.com/AGeekInside/fastapi-jinja.

Installation

Simply pip install fastapi_chameleon.

Usage

This is easy to use. Just create a folder within your web app to hold the templates such as:

├── main.py
├── views.py
│
├── templates
│   ├── home
│   │   └── index.pt
│   └── shared
│       └── layout.pt

In the app startup, tell the library about the folder you wish to use:

import os
from pathlib import Path
import fastapi_chameleon

dev_mode = True

BASE_DIR = Path(__file__).resolve().parent
template_folder = str(BASE_DIR / 'templates')
fastapi_chameleon.global_init(template_folder, auto_reload=dev_mode)

Then just decorate the FastAPI view methods (works on sync and async methods):

@router.post('/')
@fastapi_chameleon.template('home/index.pt')
async def home_post(request: Request):
    form = await request.form()
    vm = PersonViewModel(**form) 

    return vm.dict() # {'first':'Michael', 'last':'Kennedy', ...}

The view method should return a dict to be passed as variables/values to the template.

If a fastapi.Response is returned, the template is skipped and the response along with status_code and other values is directly passed through. This is common for redirects and error responses not meant for this page template.

Friendly 404s and errors

A common technique for user-friendly sites is to use a custom HTML page for 404 responses. This is especially important in FastAPI because FastAPI returns a 404 response + JSON by default. This library has support for friendly 404 pages using the fastapi_chameleon.not_found() function.

Here's an example:

@router.get('/catalog/item/{item_id}')
@fastapi_chameleon.template('catalog/item.pt')
async def item(item_id: int):
    item = service.get_item_by_id(item_id)
    if not item:
        fastapi_chameleon.not_found()
    
    return item.dict()

This will render a 404 response with using the template file templates/errors/404.pt. You can specify another template to use for the response, but it's not required.

Comments
  • Pip Install Fails with SetuptoolsDeprecationWarning

    Pip Install Fails with SetuptoolsDeprecationWarning

    I'm running a virtual environment with the latest Python from Homebrew on macOS:

    python --version
    Python 3.9.2
    

    I'm taking the FastAPI Web App course and I'm trying to install fastapi-chameleon from both this repo and my fork both error out:

     silversaucer> pip install git+https://github.com/mikeckennedy/fastapi-chameleon
    
    Collecting git+https://github.com/mikeckennedy/fastapi-chameleon
      Cloning https://github.com/mikeckennedy/fastapi-chameleon to /private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-req-build-mp91okuf
      Running command git clone -q https://github.com/mikeckennedy/fastapi-chameleon /private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-req-build-mp91okuf
        ERROR: Command errored out with exit status 1:
         command: /Users/prcutler/workspace/silversaucer/.venvfastapi/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-req-build-mp91okuf/setup.py'"'"'; __file__='"'"'/private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-req-build-mp91okuf/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-pip-egg-info-z72h9h4o
             cwd: /private/var/folders/y8/p92hytfj04b7_92zv1ppn1s00000gn/T/pip-req-build-mp91okuf/
        Complete output (7 lines):
        Traceback (most recent call last):
          File "<string>", line 1, in <module>
          File "/Users/prcutler/workspace/silversaucer/.venvfastapi/lib/python3.9/site-packages/setuptools/__init__.py", line 14, in <module>
            from setuptools.dist import Distribution
          File "/Users/prcutler/workspace/silversaucer/.venvfastapi/lib/python3.9/site-packages/setuptools/dist.py", line 30, in <module>
            from . import SetuptoolsDeprecationWarning
        ImportError: cannot import name 'SetuptoolsDeprecationWarning' from partially initialized module 'setuptools' (most likely due to a circular import) (/Users/prcutler/workspace/silversaucer/.venvfastapi/lib/python3.9/site-packages/setuptools/__init__.py)
        ----------------------------------------
    WARNING: Discarding git+https://github.com/mikeckennedy/fastapi-chameleon. Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
    ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
    

    All my Python modules appear up to date:

    Screen Shot 2021-03-10 at 10 38 14 AM

    Is there something I'm missing or I should try? Is there any other info needed?

    Thanks!

    opened by prcutler 12
  • Call to global_init() required to run any test individually

    Call to global_init() required to run any test individually

    Each specific rest requires global_init() to be ran before it can be run. The test_can_call_init_with_good_path() test does this, and that appears to enable all other tests to run.

    def test_can_call_init_with_good_path():
        fc.global_init(folder, cache_init=False)
    

    If you run various tests individually, they error out because global_init is not called. I am working on a fix for the fastapi-jinja library that uses a pytest fixture to make this work. I am basically creating a fixture that is like this:

    @pytest.fixture
    def call_global_init():
        here = os.path.dirname(__file__)
        folder = os.path.join(here, "templates")
        fc.global_init(folder, cache_init=False)
    

    The fixture could then be added to any test that requires global init to be called. It should work, but I want to verify.

    opened by AGeekInside 5
  • Is it possible to add a custom header to the response?

    Is it possible to add a custom header to the response?

    How can I add a custom response header when using fastapi-chameleon?

    If I use chameleon "manually", the response header is added as expected:

    @router.get("/headers-1/", response_class=HTMLResponse)
    async def get_headers_1(request: Request, response: Response):
        response.headers["HX-Refresh"] = "true"
        template = templates["hello.pt"]
        return template.render(name="Alice")
    

    But when I use the template decorator, the "HX-Refresh" header is not included in the response:

    @router.get("/headers-2/")
    @template(template_file='hello.pt')
    async def get_headers_2(request: Request, response: Response):
        response.headers["HX-Refresh"] = "true"
        return {'name': "Bob"}
    

    My hello.py contains

    <div>Hello, ${name}.</div>
    
    opened by Cecron 3
  • template folder not found

    template folder not found

    I have my templates folder within the same directory as my main.py file from which I am calling the templates folder.

    The code in the main.py looks like this:

    import fastapi
    import uvicorn
    import fastapi_chameleon
    from fastapi_chameleon import template
    
    
    app = fastapi.FastAPI()
    fastapi_chameleon.global_init("templates")
    
    
    @app.get("/")
    @template(template_file="index.html")
    def index():
        return {
            "user_name": "usernameExample"
        }
    
    
    if __name__ == "__main__":
        uvicorn.run(app)
    else:
        pass
    

    I get an error message:

    "c:/Users/username/OneDrive - Dynatrace/Pictures/pypi/application/venv/Scripts/python.exe" "c:/Users/username/OneDrive - Dynatrace/Pictures/pypi/application/main.py"
    Traceback (most recent call last):
      File "c:\Users\username\OneDrive - Dynatrace\Pictures\pypi\application\main.py", line 8, in <module>
        fastapi_chameleon.global_init(".templates")
      File "C:\Users\username\OneDrive - Dynatrace\Pictures\pypi\application\venv\lib\site-packages\fastapi_chameleon\engine.py", line 27, in global_init
        raise FastAPIChameleonException(msg)
    fastapi_chameleon.exceptions.FastAPIChameleonException: The specified template folder must be a folder, it's not: templates
    

    On Windows

    opened by CruzanCaramele 2
  • Releasing this to PiPy

    Releasing this to PiPy

    Hi Michael; thanks for creating this. Is there a checklist you may have around to understand what would be needed to release this package into the wild? even if it is a beta version for the time being?

    Also, would you need help with that? I am happy to help.

    opened by fferegrino 2
  • TypeError: expected str, bytes or os.PathLike object, not NoneType

    TypeError: expected str, bytes or os.PathLike object, not NoneType

    From a student:

    In ch4, when I try to run your main.py, I get this:

    C:/Users/user/FS-FAPI/source/web-applications-with-fastapi-course/code/ch4-templates/main.py
    Traceback (most recent call last):
      File "C:\Users\user\FS-FAPI\source\web-applications-with-fastapi-course\code\ch4-templates\main.py", line 7, in <module>
        from views import home
      File "C:\Users\user\FS-FAPI\source\web-applications-with-fastapi-course\code\ch4-templates\views\home.py", line 9, in <module>
        def index():
      File "C:\Users\user\FS-FAPI\venv\lib\site-packages\fastapi_chameleon\engine.py", line 72, in response_inner
        if not os.path.exists(os.path.join(template_path, template_file)):
      File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\ntpath.py", line 78, in join
        path = os.fspath(path)
    TypeError: expected str, bytes or os.PathLike object, not NoneType
    
    Process finished with exit code 1
    

    Looks a timing issue between calling global init and "using" the decorator.

    opened by mikeckennedy 2
  • Error in engine.py, using python3.9 (posixpath.py) when template_path is None

    Error in engine.py, using python3.9 (posixpath.py) when template_path is None

    Hello, got this error trying to run using ch5-viewmodels from TalkPython Course... template_path is None.

    I believe the problem is that fastapi_chameleon.global_init() is called after from fastapi_chameleon import template in home.py

    Exception has occurred: TypeError       (note: full exception trace is shown but execution is paused at: _run_module_as_main)
    expected str, bytes or os.PathLike object, not NoneType
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/posixpath.py", line 76, in join
        a = os.fspath(a)
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/site-packages/fastapi_chameleon/engine.py", line 72, in response_inner
        if not os.path.exists(os.path.join(template_path, template_file)):
      File "/home/cristiana/AAA-wip/Cursos-Programming/Curso-TalkPython-FastAPI-FullWeb/web-applications-with-fastapi-cris/views/home.py", line 13, in <module>
        def index(request: Request):
      File "/home/cristiana/AAA-wip/Cursos-Programming/Curso-TalkPython-FastAPI-FullWeb/web-applications-with-fastapi-cris/main.py", line 7, in <module>
        from views import home
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/runpy.py", line 87, in _run_code
        exec(code, run_globals)
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/runpy.py", line 97, in _run_module_code
        _run_code(code, mod_globals, init_globals,
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/runpy.py", line 268, in run_path
        return _run_module_code(code, init_globals, run_name,
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/runpy.py", line 87, in _run_code
        exec(code, run_globals)
      File "/home/cristiana/anaconda3/envs/fastapi/lib/python3.9/runpy.py", line 197, in _run_module_as_main (Current frame)
        return _run_code(code, main_globals, None,
    
    opened by Cristianasp 2
  • Test on only Python 3.6+

    Test on only Python 3.6+

    With FastAPI requiring Python 3.6+ there isnt a need to test on Python 2.7 or the other older versions of Python. You could then remove the dev dependency on six.

    opened by jmtaysom 1
  • Use fixtures in tests

    Use fixtures in tests

    Now each test is isolated and can be run on its own thanks to pytest fixtures as discussed in https://github.com/mikeckennedy/fastapi-chameleon/issues/13.

    opened by fferegrino 1
  • returning empty dict

    returning empty dict

    In what situation do you want to return an empty dict in this case? Seems like you'd more likely want to try to coerce to a dict and fail over to an empty dict? Though maybe you are just trying to account for None, in which case maybe it should be an elif response_val is None?

    try:
        model = dict(response_val)
    except TypeError:
        model = {}
    

    https://github.com/mikeckennedy/fastapi-chameleon/blob/4ffff6b14e5467fd802940fb32dba6b1f35d6d85/fastapi_chameleon/engine.py#L89-L90

    opened by falkben 1
  • Allow for both .pt and .html extensions on template file defaults

    Allow for both .pt and .html extensions on template file defaults

    Currently, we guess at the template name if not supplied:

    templates/module_name/view_method_name.pt

    Let's also look for templates/module_name/view_method_name.html and use whichever one we find.

    enhancement 
    opened by mikeckennedy 0
  • render template inside view method

    render template inside view method

    I'm trying to build a video collector similar to the one from your HTMX course. Mine uses FastAPI, Chameleon, Beanie. I'm having trouble rendering the search results due to the conditional partial render inside of the view method. I don't know how to render a template when it's inside the view method like that, only done it through the decorator on the view method function itself.

    How would I implement something like this in fastapi-chameleon?

    html = flask.render_template('videos/partials/search_results.html', videos=vm.videos)
    return flask.make_response(html)
    

    I have tried the following with from fastapi_chameleon import template imported

    html = template(template_file="videos/partials/search_results.pt", videos=vm.videos)
    return fastapi.responses.HTMLResponse(html)
    

    I also tried getting creative and reading through your fastapi-chameleon source, so I tried

    page: PageTemplate = "/usr/home/roller/Projects/coach-aaron/templates/videos/partials/search_results.pt"
    return page.render(encoding='utf-8', videos=vm.videos)
    

    That didn't work either.

    At the moment I'm going to try following the FastAPI docs If I have to for now I'll do just this one partial using Jinja if I can get it to do that.. we'll see. I appreciate any help or suggestions you may have on this. I'll definitely submit this webapp I'm working on to the Student Creation Showcase once it's ready enough and is actually doing interesting things.

    opened by possnfiffer 5
  • Added a more generic error reporting function: generic_error().

    Added a more generic error reporting function: generic_error().

    First of all, thanks for this great piece of code. Made my life a lot easier by eliminating a lot of boilerplate code. :) :+1:

    I'm working o a project right now that requires me to perform certain authorization operations on various operations. Some operations are permitted only to a certain group of people. For example it would make sense to return a 401 to a user if they are not part of a specific privileged group. Or, maybe there is something specific about a request that makes it simply off-limits always. And in that case returning 403 would fit even better.

    Please, take a look at this PR. It's introducing a new function generic_error() that allows the user to specify particular details about the error they want to return to the user. For the above examples I'd be able to e.g.:

    return fastapi_chameleon.generic_error("errors/my_unauthorized_template.ts", 401)
    

    Works just like not_found(), but allows to specify the status code.

    opened by Prezu 0
Owner
Michael Kennedy
A Python enthusiast, and an entrepreneur. Host of @talkpython and pythonbytes, founder of Talk Python Training. Python Software Foundation Fellow.
Michael Kennedy
Adds simple SQLAlchemy support to FastAPI

FastAPI-SQLAlchemy FastAPI-SQLAlchemy provides a simple integration between FastAPI and SQLAlchemy in your application. It gives access to useful help

Michael Freeborn 465 Jan 7, 2023
Keycloak integration for Python FastAPI

FastAPI Keycloak Integration Documentation Introduction Welcome to fastapi-keycloak. This projects goal is to ease the integration of Keycloak (OpenID

Code Specialist 113 Dec 31, 2022
🚀 Cookiecutter Template for FastAPI + React Projects. Using PostgreSQL, SQLAlchemy, and Docker

FastAPI + React · A cookiecutter template for bootstrapping a FastAPI and React project using a modern stack. Features FastAPI (Python 3.8) JWT authen

Gabriel Abud 1.4k Jan 2, 2023
The template for building scalable web APIs based on FastAPI, Tortoise ORM and other.

FastAPI and Tortoise ORM. Powerful but simple template for web APIs w/ FastAPI (as web framework) and Tortoise-ORM (for working via database without h

prostomarkeloff 95 Jan 8, 2023
Cookiecutter template for FastAPI projects using: Machine Learning, Poetry, Azure Pipelines and Pytests

cookiecutter-fastapi In order to create a template to FastAPI projects. ?? Important To use this project you don't need fork it. Just run cookiecutter

Arthur Henrique 225 Dec 28, 2022
🚀 Cookiecutter Template for FastAPI + React Projects. Using PostgreSQL, SQLAlchemy, and Docker

FastAPI + React · A cookiecutter template for bootstrapping a FastAPI and React project using a modern stack. Features FastAPI (Python 3.8) JWT authen

Gabriel Abud 448 Feb 19, 2021
🐍 Simple FastAPI template with factory pattern architecture

Description This is a minimalistic and extensible FastAPI template that incorporates factory pattern architecture with divisional folder structure. It

Redowan Delowar 551 Dec 24, 2022
FastAPI + Postgres + Docker Compose + Heroku Deploy Template

FastAPI + Postgres + Docker Compose + Heroku Deploy ⚠️ For educational purpose only. Not ready for production use YET Features FastAPI with Postgres s

DP 12 Dec 27, 2022
FastAPI CRUD template using Deta Base

Deta Base FastAPI CRUD FastAPI CRUD template using Deta Base Setup Install the requirements for the CRUD: pip3 install -r requirements.txt Add your D

Sebastian Ponce 2 Dec 15, 2021
FastAPI Project Template

The base to start an openapi project featuring: SQLModel, Typer, FastAPI, JWT Token Auth, Interactive Shell, Management Commands.

A.Freud 4 Dec 5, 2022
:rocket: CLI tool for FastAPI. Generating new FastAPI projects & boilerplates made easy.

Project generator and manager for FastAPI. Source Code: View it on Github Features ?? Creates customizable project boilerplate. Creates customizable a

Yagiz Degirmenci 1k Jan 2, 2023
Simple FastAPI Example : Blog API using FastAPI : Beginner Friendly

fastapi_blog FastAPI : Simple Blog API with CRUD operation Steps to run the project: git clone https://github.com/mrAvi07/fastapi_blog.git cd fastapi-

Avinash Alanjkar 1 Oct 8, 2022
Пример использования GraphQL Ariadne с FastAPI и сравнение его с GraphQL Graphene FastAPI

FastAPI Ariadne Example Пример использования GraphQL Ariadne с FastAPI и сравнение его с GraphQL Graphene FastAPI - GitHub ###Запуск на локальном окру

ZeBrains Team 9 Nov 10, 2022
Sample-fastapi - A sample app using Fastapi that you can deploy on App Platform

Getting Started We provide a sample app using Fastapi that you can deploy on App

Erhan BÜTE 2 Jan 17, 2022
Flask-vs-FastAPI - Understanding Flask vs FastAPI Web Framework. A comparison of two different RestAPI frameworks.

Flask-vs-FastAPI Understanding Flask vs FastAPI Web Framework. A comparison of two different RestAPI frameworks. IntroductionIn Flask is a popular mic

Mithlesh Navlakhe 1 Jan 1, 2022
fastapi-admin2 is an upgraded fastapi-admin, that supports ORM dialects, true Dependency Injection and extendability

FastAPI2 Admin Introduction fastapi-admin2 is an upgraded fastapi-admin, that supports ORM dialects, true Dependency Injection and extendability. Now

Glib 14 Dec 5, 2022
Code Specialist 27 Oct 16, 2022
FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by django-admin, and has as many powerful functions as django-admin.

简体中文 | English 项目介绍 FastAPI-Amis-Admin fastapi-amis-admin是一个拥有高性能,高效率,易拓展的fastapi管理后台框架. 启发自Django-Admin,并且拥有不逊色于Django-Admin的强大功能. 源码 · 在线演示 · 文档 · 文

AmisAdmin 318 Dec 31, 2022
Adds GraphQL support to your Flask application.

Flask-GraphQL Adds GraphQL support to your Flask application. Usage Just use the GraphQLView view from flask_graphql from flask import Flask from flas

GraphQL Python 1.3k Dec 31, 2022