diff --git a/.gitignore b/.gitignore index 21d0b89..fe6bd50 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .venv/ +**/__pycache__/ diff --git a/404.html b/404.html index fd3425f..5fde9e5 100644 --- a/404.html +++ b/404.html @@ -243,13 +243,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/authentications/index.html b/authentications/index.html index a631dac..4ff825a 100644 --- a/authentications/index.html +++ b/authentications/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/benchmarks/fastapi/main.py b/benchmarks/fastapi/main.py index 6e30233..aa94710 100644 --- a/benchmarks/fastapi/main.py +++ b/benchmarks/fastapi/main.py @@ -1,8 +1,11 @@ from fastapi import FastAPI -from fastapi.responses import Response +from fastapi.responses import JSONResponse + + app = FastAPI() -@app.get("/") +@app.get('/') async def root(): - return Response(status_code=200) + data = {'detail': 'hello world'} + return JSONResponse(content=data, status_code=200) diff --git a/benchmarks/fastapi/requirements.txt b/benchmarks/fastapi/requirements.txt index 2ab0a56..df19e84 100644 --- a/benchmarks/fastapi/requirements.txt +++ b/benchmarks/fastapi/requirements.txt @@ -1,2 +1,3 @@ -fastapi==0.93.0 -uvicorn==0.20.0 \ No newline at end of file +fastapi==0.100.1 +uvicorn==0.23.2 +uvloop==0.17.0 \ No newline at end of file diff --git a/benchmarks/index.html b/benchmarks/index.html index 76fddb1..319ec34 100644 --- a/benchmarks/index.html +++ b/benchmarks/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/benchmarks/panther/core/apis.py b/benchmarks/panther/core/apis.py index 9dbfb97..a0cf0db 100644 --- a/benchmarks/panther/core/apis.py +++ b/benchmarks/panther/core/apis.py @@ -5,4 +5,5 @@ @API() async def hello_world(): - return Response(status_code=status.HTTP_200_OK) + data = {'detail': 'hello world'} + return Response(data=data, status_code=status.HTTP_200_OK) diff --git a/benchmarks/panther/core/urls.py b/benchmarks/panther/core/urls.py index 0dc6e64..554b89f 100644 --- a/benchmarks/panther/core/urls.py +++ b/benchmarks/panther/core/urls.py @@ -2,5 +2,5 @@ urls = { - '': hello_world, + '/': hello_world, } diff --git a/benchmarks/panther/logs/main.log b/benchmarks/panther/logs/main.log new file mode 100644 index 0000000..e04cb23 --- /dev/null +++ b/benchmarks/panther/logs/main.log @@ -0,0 +1,4 @@ +2023-08-05 00:26:06 | Base directory: /home/ali/dev/panther/docs/docs/benchmarks/panther +2023-08-05 00:26:06 | Configs loaded. +2023-08-05 00:32:11 | Base directory: /home/ali/dev/panther/docs/docs/benchmarks/panther +2023-08-05 00:32:11 | Configs loaded. diff --git a/benchmarks/panther/logs/monitoring.log b/benchmarks/panther/logs/monitoring.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/panther/logs/query.log b/benchmarks/panther/logs/query.log new file mode 100644 index 0000000..e69de29 diff --git a/benchmarks/panther/requirements.txt b/benchmarks/panther/requirements.txt index f070805..4eef7a2 100644 --- a/benchmarks/panther/requirements.txt +++ b/benchmarks/panther/requirements.txt @@ -1 +1,2 @@ -panther==1.7.13 \ No newline at end of file +panther==2.1.0 +uvloop==0.17.0 \ No newline at end of file diff --git a/benchmarks/panther/run.sh b/benchmarks/panther/run.sh index d110279..a57d6eb 100644 --- a/benchmarks/panther/run.sh +++ b/benchmarks/panther/run.sh @@ -1 +1 @@ -panther run \ No newline at end of file +uvicorn main:app --no-access-log \ No newline at end of file diff --git a/caching/index.html b/caching/index.html index 0b916f4..b1dbca3 100644 --- a/caching/index.html +++ b/caching/index.html @@ -249,13 +249,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/class_first_crud/index.html b/class_first_crud/index.html new file mode 100644 index 0000000..24c312a --- /dev/null +++ b/class_first_crud/index.html @@ -0,0 +1,1357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Class Base - Panther + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + +
    +
    +
    + + + + + + + +
    +
    + + + + + + + +

    Class Base

    + +

    We assume you could run the project with Introduction

    +

    Now let's write custom API Create, Retrieve, Update and Delete for a Book:

    +

    Structure & Requirements#

    +

    Create Model#

    +

    Create a model named Book in app/models.py:

    +
    from panther.db import Model
    +
    +
    +class Book(Model):
    +    name: str
    +    author: str
    +    pages_count: int
    +
    +

    Create API Class#

    +

    Create the BookAPI() in app/apis.py:

    +
    from panther.app import GenericAPI
    +
    +
    +class BookAPI(GenericAPI):
    +    ... 
    +
    +
    +

    We are going to complete it later ...

    +
    +

    Update URLs#

    +

    Add the BookAPI in app/urls.py:

    +
    from app.apis import BookAPI
    +
    +
    +urls = {
    +    'book/': BookAPI,
    +}
    +
    +

    We assume that the urls in core/urls.py pointing to app/urls.py, like below:

    +
    from app.urls import urls as app_urls
    +
    +
    +urls = {
    +    '/': app_urls,
    +}
    +
    +

    Add Database Middleware#

    +

    Add one database middleware in core/configs.py MIDDLEWARES, we are going to add pantherdb

    +
    +

    PantherDB is a Simple, FileBase and Document Oriented database:

    +
    +
    ...
    +
    +MIDDLEWARES = [
    +    ('panther.middlewares.db.Middleware', {'url': f'pantherdb://{BASE_DIR}/{DB_NAME}.pdb'}),
    +]
    +
    +

    APIs#

    +

    API - Create a Book#

    +

    Now we are going to create a book on post request, We need to:

    +
      +
    1. +

      Declare post method in BookAPI: +

      from panther.app import GenericAPI
      +
      +
      +class BookAPI(GenericAPI):
      +
      +    def post(self):
      +        ...
      +

      +
    2. +
    3. +

      Declare request: Request in BookAPI.post() function: +

      from panther.app import GenericAPI
      +from panther.request import Request
      +
      +
      +class BookAPI(GenericAPI):
      +
      +    def post(self, request: Request):
      +        ...
      +

      +
    4. +
    5. +

      Create serializer in app/serializers.py, we used pydantic for the validation of request.data : +

      from pydantic import BaseModel
      +
      +
      +class BookSerializer(BaseModel):
      +    name: str
      +    author: str
      +    pages_count: int
      +

      +
    6. +
    7. +

      Pass the created serializer to our BookAPI as input_model so the incoming data will be validated and cleaned automatically:

      +

      from panther.app import GenericAPI
      +from panther.request import Request
      +
      +from app.serializers import BookSerializer
      +
      +
      +class BookAPI(GenericAPI):
      +    input_model = BookSerializer
      +
      +    def post(self, request: Request):
      +        ...
      +
      + Now we have access to request.data, We are going to use it like the below for ease of use, so the auto-suggest helps us in development:

      +
      from panther.app import GenericAPI
      +from panther.request import Request
      +
      +from app.serializers import BookSerializer
      +
      +
      +class BookAPI(GenericAPI):
      +    input_model = BookSerializer
      +
      +    def post(self, request: Request):
      +        body: BookSerializer = request.data
      +        ...
      +
      +
    8. +
    9. +

      Now we have access to the validated data, and we can create our first book:

      +
      from panther.app import GenericAPI
      +from panther.request import Request
      +
      +from app.serializers import BookSerializer
      +from app.models import Book
      +
      +
      +class BookAPI(GenericAPI):
      +    input_model = BookSerializer
      +
      +    def post(self, request: Request):
      +        body: BookSerializer = request.data
      +        Book.insert_one(
      +            name=body.name,
      +            author=body.author,
      +            pages_count=body.pages_count,
      +        )
      +        ...
      +
      +
    10. +
    11. +

      And finally we return 201 Created status_code as response of post: +

      from panther import status
      +from panther.app import GenericAPI
      +from panther.request import Request
      +from panther.response import Response
      +
      +from app.serializers import BookSerializer
      +from app.models import Book
      +
      +
      +class BookAPI(GenericAPI):
      +    input_model = BookSerializer
      +
      +    def post(self, request: Request):
      +        body: BookSerializer = request.data
      +        book = Book.insert_one(
      +            name=body.name,
      +            author=body.author,
      +            pages_count=body.pages_count,
      +        )
      +        return Response(data=book, status_code=status.HTTP_201_CREATED)
      +

      +
    12. +
    +
    +

    The response.data can be Instance of Models, dict, str, tuple, list, str or None

    +

    Panther will return None if you don't return anything as response.

    +
    +

    API - List of Books#

    +

    We just need to add another method for GET method and return the lists of books:

    +
    from panther import status
    +from panther.app import GenericAPI
    +from panther.request import Request
    +from panther.response import Response
    +
    +from app.serializers import BookSerializer
    +from app.models import Book
    +
    +
    +class BookAPI(GenericAPI):
    +    input_model = BookSerializer
    +
    +    def post(self, request: Request):
    +        ...
    +
    +    def get(self):
    +        books: list[Book] = Book.find()
    +        return Response(data=books, status_code=status.HTTP_200_OK)
    +
    +
    +

    Panther validate input with input_model, only in POST, PUT, PATCH methods.

    +
    +

    Filter Response Fields#

    +

    Assume we don't want to return field author in response:

    +
      +
    1. +

      Create new serializer in app/serializers.py:

      +
      from pydantic import BaseModel
      +
      +
      +class BookOutputSerializer(BaseModel):
      +    name: str
      +    pages_count: int
      +
      +
    2. +
    3. +

      Add the BookOutputSerializer as output_model to your class

      +
      from panther import status
      +from panther.app import GenericAPI
      +from panther.request import Request
      +from panther.response import Response
      +
      +from app.serializers import BookSerializer, BookOutputSerializer
      +from app.models import Book
      +
      +
      +class BookAPI(GenericAPI):
      +    input_model = BookSerializer
      +    output_model = BookOutputSerializer
      +
      +    def post(self, request: Request):
      +        ...
      +
      +    def get(self):
      +        books: list[Book] = Book.find()
      +        return Response(data=books, status_code=status.HTTP_200_OK)
      +
      +
    4. +
    +
    +

    Panther use the output_model, in all methods.

    +
    +

    Cache The Response#

    +

    For caching the response, we should add cache=True in API(). +And it will return the cached response every time till cache_exp_time

    +

    For setting a custom expiration time for API we need to add cache_exp_time to API():

    +
    from datetime import timedelta
    +
    +from panther import status
    +from panther.app import GenericAPI
    +from panther.request import Request
    +from panther.response import Response
    +
    +from app.serializers import BookSerializer, BookOutputSerializer
    +from app.models import Book
    +
    +
    +class BookAPI(GenericAPI):
    +    input_model = BookSerializer
    +    output_model = BookOutputSerializer
    +    cache = True
    +    cache_exp_time = timedelta(seconds=10)
    +
    +    def post(self, request: Request):
    +        ...
    +
    +    def get(self):
    +        books: list[Book] = Book.find()
    +        return Response(data=books, status_code=status.HTTP_200_OK)
    +
    +
    +

    Panther is going to use the DEFAULT_CACHE_EXP from core/configs.py if cache_exp_time has not been set.

    +
    +

    Throttle The Request#

    +

    For setting rate limit for requests, we can add throttling to BookAPI, it should be the instance of panther.throttling.Throttling, +something like below (in the below example user can't request more than 10 times in a minutes):

    +
    from datetime import timedelta
    +
    +from panther import status
    +from panther.app import GenericAPI
    +from panther.request import Request
    +from panther.response import Response
    +from panther.throttling import Throttling
    +
    +from app.serializers import BookSerializer, BookOutputSerializer
    +from app.models import Book
    +
    +
    +class BookAPI(GenericAPI):
    +    input_model = BookSerializer
    +    output_model = BookOutputSerializer
    +    cache = True
    +    cache_exp_time = timedelta(seconds=10)
    +    throttling = Throttling(rate=10, duration=timedelta(minutes=1))
    +
    +    def post(self, request: Request):
    +        ...
    +
    +    def get(self):
    +        books: list[Book] = Book.find()
    +        return Response(data=books, status_code=status.HTTP_200_OK)
    +
    +

    API - Retrieve a Book#

    +

    For retrieve, update and delete API, we are going to

    +
      +
    1. +

      Create another class named SingleBookAPI in app/apis.py:

      +
      from panther.app import GenericAPI
      +
      +
      +class SingleBookAPI(GenericAPI):
      +    ...
      +
      +
    2. +
    3. +

      Add it in app/urls.py:

      +
      from app.apis import BookAPI, SingleBookAPI
      +
      +
      +urls = {
      +    'book/': BookAPI,
      +    'book/<book_id>/': SingleBookAPI,
      +}
      +
      +
    4. +
    +
    +

    You should write the Path Variable in < and >

    +

    You should have the parameter with the same name of path variable in you api with normal type hints

    +

    Panther will convert type of the path variable to your parameter type, then pass it

    +
    +
      +
    1. +

      Complete the api:

      +
      from panther import status
      +from panther.app import GenericAPI
      +from panther.response import Response
      +
      +from app.models import Book
      +
      +
      +class SingleBookAPI(GenericAPI):
      +
      +    def get(self, book_id: int):
      +        if book := Book.find_one(id=book_id):
      +            return Response(data=book, status_code=status.HTTP_200_OK)
      +        else:
      +            return Response(status_code=status.HTTP_404_NOT_FOUND)
      +
      +
    2. +
    +

    API - Update a Book#

    +
      +
    • +

      We can update in several ways:

      +
        +
      1. +

        Update a document

        +
        from panther import status
        +from panther.app import GenericAPI
        +from panther.request import Request
        +from panther.response import Response
        +
        +from app.models import Book
        +from app.serializers import BookSerializer
        +
        +
        +class SingleBookAPI(GenericAPI):
        +    input_model = BookSerializer
        +
        +    def get(self, book_id: int):
        +        ...
        +
        +    def put(self, request: Request, book_id: int):
        +        body: BookSerializer = request.data
        +
        +        book: Book = Book.find_one(id=book_id)
        +        book.update(
        +            name=body.name, 
        +            author=body.author, 
        +            pages_count=body.pages_count
        +        )
        +        return Response(status_code=status.HTTP_202_ACCEPTED)
        +
        +
      2. +
      3. +

        Update with update_one query

        +
        from panther import status
        +from panther.app import GenericAPI
        +from panther.request import Request
        +from panther.response import Response
        +
        +from app.models import Book
        +from app.serializers import BookSerializer
        +
        +
        +class SingleBookAPI(GenericAPI):
        +    input_model = BookSerializer
        +
        +    def get(self, book_id: int):
        +        ...
        +
        +    def put(self, request: Request, book_id: int):
        +        is_updated: bool = Book.update_one({'id': book_id}, request.data.dict())
        +        data = {'is_updated': is_updated}
        +        return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
        +
        +
      4. +
      5. +

        Update with update_many query

        +
        from panther import status
        +from panther.app import GenericAPI
        +from panther.request import Request
        +from panther.response import Response
        +
        +from app.models import Book
        +from app.serializers import BookSerializer
        +
        +
        +class SingleBookAPI(GenericAPI):
        +    input_model = BookSerializer
        +
        +    def get(self, book_id: int):
        +        ...
        +
        +    def put(self, request: Request, book_id: int):
        +        updated_count: int = Book.update_many({'id': book_id}, request.data.dict())
        +        data = {'updated_count': updated_count}
        +        return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
        +
        +
      6. +
      +
      +

      You can handle the PATCH the same way as PUT

      +
      +
    • +
    +

    API - Delete a Book#

    +
      +
    • +

      We can delete in several ways too:

      +
        +
      1. +

        Delete a document

        +

            from panther import status
        +    from panther.app import GenericAPI
        +    from panther.request import Request
        +    from panther.response import Response
        +
        +    from app.models import Book
        +
        +    class SingleBookAPI(GenericAPI):
        +        input_model = BookSerializer
        +
        +        def get(self, book_id: int):
        +            ...
        +
        +        def put(self, request: Request, book_id: int):
        +            ...
        +
        +        def delete(self, book_id: int):
        +            is_deleted: bool = Book.delete_one(id=book_id)
        +            if is_deleted:
        +                return Response(status_code=status.HTTP_204_NO_CONTENT)
        +            else:
        +                return Response(status_code=status.HTTP_400_BAD_REQUEST)
        +
        + 2. Delete with delete_one query

        +
            from panther import status
        +    from panther.app import GenericAPI
        +    from panther.request import Request
        +    from panther.response import Response
        +
        +    from app.models import Book
        +
        +
        +    class SingleBookAPI(GenericAPI):
        +        input_model = BookSerializer
        +
        +        def get(self, book_id: int):
        +            ...
        +
        +        def put(self, request: Request, book_id: int):
        +            ...
        +
        +        def delete(self, book_id: int):
        +            is_deleted: bool = Book.delete_one(id=book_id)
        +            return Response(status_code=status.HTTP_204_NO_CONTENT)
        +
        +
      2. +
      3. +

        Delete with delete_many query

        +
            from panther import status
        +    from panther.app import GenericAPI
        +    from panther.request import Request
        +    from panther.response import Response
        +
        +    from app.models import Book
        +
        +
        +    class SingleBookAPI(GenericAPI):
        +        input_model = BookSerializer
        +
        +        def get(self, book_id: int):
        +            ...
        +
        +        def put(self, request: Request, book_id: int):
        +            ...
        +
        +        def delete(self, book_id: int):
        +            deleted_count: int = Book.delete_many(id=book_id)
        +            return Response(status_code=status.HTTP_204_NO_CONTENT)
        +
        +
      4. +
      +
    • +
    + + + + + + +
    +
    + + +
    + +
    + + + +
    +
    +
    +
    + + + + + + + + + \ No newline at end of file diff --git a/configs/index.html b/configs/index.html index 62796e6..a166f9a 100644 --- a/configs/index.html +++ b/configs/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/first_crud/index.html b/function_first_crud/index.html similarity index 98% rename from first_crud/index.html rename to function_first_crud/index.html index bf139df..363ff58 100644 --- a/first_crud/index.html +++ b/function_first_crud/index.html @@ -10,20 +10,20 @@ - + - + - First Crud - Panther + Function Base - Panther @@ -110,7 +110,7 @@
    - First Crud + Function Base
    @@ -256,6 +256,35 @@ + +
  • + + + + + + + + + + + +
  • + + @@ -749,7 +799,7 @@ -

    First Crud

    +

    Function Base

    We assume you could run the project with Introduction

    Now let's write custom API Create, Retrieve, Update and Delete for a Book:

    diff --git a/index.html b/index.html index 7d494ee..e9fe5f5 100644 --- a/index.html +++ b/index.html @@ -14,7 +14,7 @@ - + @@ -377,13 +377,61 @@ + +
  • + + + + + + + + + + + +
  • + + @@ -721,16 +769,15 @@

    Introduction

    Panther#

    Is A Fast & Friendly Web Framework For Building Async APIs With Python 3.11+

    -

    +

    logo

    logo - Supported by JetBrain + Supported by JetBrain

    -

    Full Documentation: https://pantherpy.github.io

    GitHub: https://github.com/AliRn76/panther

    PyPI: https://pypi.org/project/panther/


    @@ -800,6 +847,7 @@

    Python Shell +

    Example#

    +

    now you can see these two urls:

    +
    -

    Writing Your First CRUD: First CRUD

    +

    Writing Your First CRUD: First CRUD

    diff --git a/log_queries/index.html b/log_queries/index.html index 3e207c9..d6f1e79 100644 --- a/log_queries/index.html +++ b/log_queries/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/middlewares/index.html b/middlewares/index.html index 198ab58..a3a4e11 100644 --- a/middlewares/index.html +++ b/middlewares/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/monitoring/index.html b/monitoring/index.html index 772c0a2..72b2035 100644 --- a/monitoring/index.html +++ b/monitoring/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/panther_odm/index.html b/panther_odm/index.html index 1a1b521..23caad9 100644 --- a/panther_odm/index.html +++ b/panther_odm/index.html @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + diff --git a/release_notes/index.html b/release_notes/index.html index 598fa22..6a4c822 100644 --- a/release_notes/index.html +++ b/release_notes/index.html @@ -75,7 +75,7 @@
    - + Skip to content @@ -254,13 +254,61 @@ + +
  • + + + + + + + + + + + +
  • + + @@ -464,6 +512,41 @@