Giter Club home page Giter Club logo

fastapi-practices / fastapi_best_architecture Goto Github PK

View Code? Open in Web Editor NEW
282.0 4.0 47.0 4.68 MB

基于 FastAPI 构建的前后端分离 RBAC 权限控制系统,采用独特的伪三层架构模型设计,内置 fastapi-admin 基本实现,并作为模板库免费开源

License: MIT License

Dockerfile 0.60% Python 99.04% Mako 0.18% Shell 0.18%
fastapi pydantic fastapi-template fastapi-sqlalchemy pycasbin pydantic-v2 fastapi-rbac fastapi-admin

fastapi_best_architecture's Introduction

FastAPI Best Architecture

GitHub Static Badge Ruff Pydantic v2

Caution

For 2024-3-22 (announcement)

The master branch has completed the app architecture refactoring, please pay extra attention to sync fork operations to avoid irreparable damage!

We have kept and locked the original branch (legacy-single-app-pydantic-v2), which you can get in the branch selector

English | 简体中文

FastAPI framework based on the front-end and back-end separation of the middle and back-end solutions, follow the pseudo three-tier architecture design, support for python3.10 and above versions

Its purpose is to allow you to use it directly as the infrastructure of your new project, this repository as a template library open to any person or enterprise can be used for free!

🔥Continuously updated and maintained🔥

Pseudo three-tier architecture

The mvc architecture is a common design pattern in python web, but the three-tier architecture is even more fascinating

In python web development, there is no common standard for the concept of three-tier architecture, so we'll call it a pseudo three-tier architecture here

But please note that we don't have a traditional multi-app structure (django, springBoot...) If you don't like this pattern, use templates to transform it to your heart's content!

workflow java fastapi_best_architecture
view controller api
data transmit dto schema
business logic service + impl service
data access dao / mapper crud
model model / entity model

Online Demo

You can view some of the preview screenshots in fastapi_best_architecture_ui

Luckily, we now have a demo site: FBA UI

username / password: admin / 123456

Features

  • Design with FastAPI PEP 593 Annotated Parameters
  • Global asynchronous design with async/await + asgiref
  • Follows Restful API specification
  • Global SQLAlchemy 2.0 syntax
  • Pydantic v1 and v2 (different branches)
  • Casbin RBAC access control model
  • Role menu RBAC access control model
  • Celery asynchronous tasks
  • JWT middleware whitelist authentication
  • Global customizable time zone time
  • Docker / Docker-compose deployment
  • Pytest Unit Testing

Built-in features

  1. User management: system user role management, permission assignment
  2. Department Management: Configure the system organization (company, department, group...)
  3. Menu Management: Configuration of system menus, user menus, button privilege identification
  4. Role Management: Assign role menu privileges, assign role routing privileges
  5. Dictionary Management: Maintain common fixed data or parameters within the system.
  6. Operation Logs: logging and querying of normal and abnormal system operations.
  7. Login Authentication: graphical authentication code background authentication login
  8. Login Logs: Logging and querying of normal and abnormal user logins
  9. Service Monitoring: server hardware device information and status
  10. Scheduled tasks: automated tasks, asynchronous tasks, and function invocation are supported
  11. Interface Documentation: Automatically generate online interactive API interface documentation.

Local development

  • Python: 3.10+
  • Mysql: 8.0+
  • Redis: The latest stable version is recommended
  • Nodejs: 14.0+

Backend

  1. Enter the backend directory

    cd backend
  2. Install the dependencies

    pip install -r requirements.txt
  3. Create a database fba with utf8mb4 encoding.

  4. Install and start Redis

  5. Create a .env file in the backend directory.

    touch .env
    
    cp .env.example .env
  6. Modify the configuration files core/conf.py and .env as needed.

  7. database migration alembic

    # Generate the migration file
    alembic revision --autogenerate
    
    # Execute the migration
    alembic upgrade head
  8. Start celery worker, beat and flower

    celery -A app.task.celery worker -l info
    
    # Scheduled tasks (optional)
    celery -A app.task.celery beat -l info
    
    # Web monitor (optional)
    celery -A app.task.celery flower --port=8555 --basic-auth=admin:123456
  9. Initialize test data (Optional)

  10. Execute the main.py file to start the service

  11. Open a browser and visit: http://127.0.0.1:8000/api/v1/docs

Front end

Jump to fastapi_best_architecture_ui View details


Docker Deployment

Warning

Default port conflicts: 8000, 3306, 6379, 5672.

It is recommended to shut down local services: mysql, redis, rabbitmq... before deployment.

  1. Go to the deploy/backend/docker-compose directory, and create the environment variable file .env.

    cd deploy/backend/docker-compose
    
    touch .env.server ../../../backend/.env
    
    cp .env.server ../../../backend/.env
  2. Modify the configuration files backend/core/conf.py and .env as needed.

  3. Execute the one-click startup command

    docker-compose up -d --build
  4. Wait for the command to complete.

  5. Open a browser and visit: http://127.0.0.1:8000/api/v1/docs

Test data

Initialize the test data using the backend/sql/init_test_data.sql file.

Development Process

(For reference only)

  1. define the database model (model)
  2. define the data validation model (schema)
  3. define the view (api) and routing (router)
  4. write business (service)
  5. write database operations (crud)

Testing

Execute unit tests through pytest.

  1. create a test database fba_test with utf8mb4 encoding

  2. create database tables using the backend/sql/create_tables.sql file

  3. initialize the test data using the backend/sql/init_pytest_data.sql file

  4. Go to the backend directory and execute the test commands.

    cd backend/
    
    pytest -vs --disable-warnings

Status

Alt

Contributors

Special thanks

Interactivity

WeChat / QQ

Sponsor us

If this program has helped you, you can sponsor us with some coffee beans: ☕ Sponsor ☕

License

This project is licensed by the terms of the MIT license

Stargazers over time

fastapi_best_architecture's People

Contributors

dependabot[bot] avatar downdawn avatar downdawn66 avatar wu-clan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

fastapi_best_architecture's Issues

raw_exc.errors() exception

When creating a department, if the mailbox format and status are incorrect, an exception will be thrown:

{
  "code": 500,
  "msg": "'permitted'",
  "data": null
}

get_token abnormal return

It's better to provide a standardized return. The default TokenError is too general.

  raise HTTPException(
      status_code=HTTP_401_UNAUTHORIZED,
      detail="Not authenticated",
      headers={"WWW-Authenticate": "Bearer"},
  )

最近还会有大的版本吗?

感谢分享,我实践了做了个爬虫项目,写起来项目结构清晰和部署简单。

但是刚做完就更新3.10后版本了,想问下还会进行大的改版吗?尤其是在项目结构上。

Add token refresh

Add token refreshing mechanism (may be related to front-end work)

get_request_args Exception

When the API request parameters are missing and request.json() fails to parse, proper error handling should be implemented.


            body_data = await request.body()
            if body_data:
                try:
                    json_data = await request.json()
                    args.update(json_data)
                except Exception as e:
                    log.exception(e)
                    args.update({'body': body_data.decode('utf-8')})

Argument 'value' has incorrect type (expected str, got UUID)

当新建用户时,遇到如下报错
File "asyncmy\converters.pyx", line 26, in asyncmy.converters.escape_item
val = encoder(val, mapping)
TypeError: Argument 'value' has incorrect type (expected str, got UUID)

asyncmy 0.2.8
Mysql 8.1.0

get_user_Info func exception

Form #4 (reply in thread)

@staticmethod
async def get_user_info(username: str):
    async with async_db_session() as db:
        user = await UserDao.get_user_by_username(db, username)
        if not user:
            raise errors.NotFoundError(msg='用户不存在')
    if user.avatar is not None:
        user.avatar = cut_path(AvatarPath + user.avatar)[1]
    # here
    return user

Abnormal Custom Form Structure in Swagger UI

The default form login return structure must be:

{"access_token": access_token, "token_type": "bearer"}

Otherwise, the swagger ui cannot request correctly, even if the verification passes

expectation is

{
  "code": 200,
  "msg": "Success",
  "data": {
        {"access_token": access_token, "token_type": "bearer"}
}
}

Add RBAC

The addition of RBAC is the main one and will include some other additions and modifications

Please note that this will be not a forward compatible change

Enum exception

The get request sets the enumeration value and throws an exception

async def get_all_menus(
    title: Annotated[str | None, Query()] = None,
    status: Annotated[StatusType | None, Query()] = None,
):
{
  "code": 422,
  "msg": "请求参数非法",
  "data": {
    "errors": [
      {
        "loc": [
          "query",
          "status"
        ],
        "msg": "value is not a valid enumeration member; permitted: 0, 1",
        "type": "type_error.enum",
        "ctx": {
          "enum_values": [
            0,
            1
          ]
        }
      }
    ]
  }
}

The post request set enumeration value is invalid

For example, when creating a menu, although the MenuType is set, it is still possible to set data exceeding the limit

GET method uses enum parameter exception

status: Annotated[StatusEnum | None, Query()] = None,

exception

{
  "code": 422,
  "msg": "请求参数非法",
  "data": {
    "errors": [
      {
        "loc": [
          "query",
          "method"
        ],
        "msg": "value is not a valid enumeration member; permitted: 0, 1, 2",
        "type": "type_error.enum",
        "ctx": {
          "enum_values": [
            0,
            1,
            2
          ]
        }
      }
    ]
  }
}

Using class views via fastapi_utils?

Hi, @downdawn

I've looked at the class view implementation of fastapi_utils in general, but I'm not very interested

# End Setup
app = FastAPI()
router = InferringRouter()  # Step 1: Create a router


@cbv(router)  # Step 2: Create and decorate a class to hold the endpoints
class ItemCBV:
    # Step 3: Add dependencies as class attributes
    session: Session = Depends(get_db)
    user_id: UserID = Depends(get_jwt_user)

    @router.post("/item")
    def create_item(self, item: ItemCreate) -> ItemInDB:
        # Step 4: Use `self.<dependency_name>` to access shared dependencies
        item_orm = ItemORM(name=item.name, owner=self.user_id)
        self.session.add(item_orm)
        self.session.commit()
        return ItemInDB.from_orm(item_orm)

For session, since we're using SQLAlchemy-2.0, we'll use async_db_session.begin() directly, this seems to make more sense

from functools import wraps

def session_decorator(func):
    @wraps(func)
    async def wrapper(*args, **kwargs):
        async with async_db_session.begin() as db:
            return await func(db, *args, **kwargs)
    return wrapper


class ApiService:

    @staticmethod
    @session_decorator
    async def get_api_list(db):
        return await paginate(db, ApiDao.get_all_apis())

For Depends(get_jwt_user), we need to make FastAPI >= 0.95.0, then use the Annotated feature, and call it globally, E.g:

CurrentUser = Annotated[User, Depends(get_current_user)]

Like in the fastapi documentation: Share Annotated dependencies

Token storage and logout

Discussed in #44

Originally posted by downdawn May 16, 2023
Hi, @wu-clan
tiangolo/fastapi#7119
tiangolo/fastapi#8959
tiangolo/fastapi#3580

None of the above discussions have provided a perfect solution.

Here is my idea:

We can use Redis to store tokens with expiration times, support token refresh, and enable features such as single-point login, multi-point login, and distributed deployment.

Tokens can also store additional information, such as role_id, company_id, is_admin, etc. This makes querying more convenient, but there may be security concerns that need to be carefully considered.

Perhaps there are open-source libraries that have already solved these issues.

get_cpu_info exception

windows10

    total = cpu_times.user + cpu_times.nice + cpu_times.system + cpu_times.idle \
AttributeError: 'scputimes' object has no attribute 'nice'

Department level changes

If there is no parent id, then the level is 1. If there is a parent, then you need to query the parent's level, and then add 1.

new_obj.update({'level': obj.parent_id + 1 if obj.parent_id else 1, 'create_user': user_id})

I understand that level is used to limit the number of layers created, but there are many complicated situations to consider here, such as updating the third level to another level, and the sub-levels under the third level should also change accordingly. etc.

celery task exception

When I was testing the task of sending an email, an exception occurred:

@celery_app.task
def send_active_email(email: str, active_url: str) -> str:
    send_verification_code_email(to=email, active_url=active_url)
    print(f'发送激活邮件到 {email} 成功')
    return 'Success'

KeyError: 'backend.app.tasks.send_active_email'

Request interface: /api/v1/mixes/tests/send

KeyError: 'backend.app.tasks.task_demo_async'

Received unregistered task of type 'backend.app.tasks.task_demo_async'.
The message has been ignored and discarded.

Did you remember to import the module containing this task?
Or maybe you're using relative imports?

Please see
https://docs.celeryq.dev/en/latest/internals/protocol.html
for more information.

BackgroundTasks task is invalid

1、Trying to log in, the background task creation login log is not executed, and the database has no data.

2、An exception occurs when logging in in json format

UnboundLocalError: local variable 'login_logs_params' referenced before assignment

opera_log_middleware message length is not enough

When an exception occurs, the length of 255 is not enough, you can change it to TEXT

msg = getattr(e, 'msg', traceback.format_exc() if settings.ENVIRONMENT == 'dev' else 'Internal Server Error')
msg: Mapped[str | None] = mapped_column(String(255), comment='提示消息')

UUID exception

Mysql5.7 uses the uuid type, and exceptions will occur during database migration.

default_factory=uuid4

AccessMiddleware and OperaLogMiddleware cannot exist at the same time.

If you turn on AccessMiddleware settings, an exception "'async for' requires an object with aiter method, got method" will occur. Besides that, should request.state.ip be set in OperaLogMiddleware? Since OperaLogMiddleware is an optional plugin, if the user disables this plugin, the auth module will throw an exception for missing state.ip because it is used by captcha.

Logic before deletion

When deleting roles in batches, it is necessary to determine whether there is associated data

Need to judge whether there is data before deleting?

Add jwt middleware

In order to use certain features, it was decided to use middleware to implement jwt authentication

Note: Make sure swagger authentication is available

Algorithm bug traversal_to_tree

The traversal_to_tree algorithm has a bug, which has a lot to do with the order. If the parent is behind the child, the data will be empty.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.