-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
37 changed files
with
1,373 additions
and
1,118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
include src/fastapi_quickcrud_codegen/model/template/common/*.jinja2 | ||
include src/fastapi_quickcrud_codegen/model/template/pydantic/*.jinja2 | ||
include src/fastapi_quickcrud_codegen/model/template/route/*.jinja2 | ||
include src/fastapi_quickcrud_codegen/model/template/sqlalchemy/*.jinja2 | ||
include src/fastapi_quickcrud_codegen/model/template/*.jinja2 |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import os | ||
|
||
from fastapi import FastAPI | ||
from sqlalchemy import ARRAY, BigInteger, Boolean, CHAR, Column, Date, DateTime, Float, Integer, \ | ||
JSON, Numeric, SmallInteger, String, Text, Time, UniqueConstraint, text | ||
from sqlalchemy.dialects.postgresql import INTERVAL, JSONB, UUID | ||
from sqlalchemy.orm import declarative_base, sessionmaker | ||
|
||
from fastapi_quickcrud_codegen import crud_router_builder, CrudMethods | ||
|
||
TEST_DATABASE_URL = os.environ.get('TEST_DATABASE_URL', 'postgresql://postgres:1234@127.0.0.1:5432/postgres') | ||
|
||
app = FastAPI() | ||
|
||
Base = declarative_base() | ||
metadata = Base.metadata | ||
|
||
from sqlalchemy import create_engine | ||
|
||
engine = create_engine(TEST_DATABASE_URL, future=True, echo=True, | ||
pool_use_lifo=True, pool_pre_ping=True, pool_recycle=7200) | ||
async_session = sessionmaker(autocommit=False, autoflush=False, bind=engine) | ||
|
||
|
||
def get_transaction_session(): | ||
try: | ||
db = async_session() | ||
yield db | ||
finally: | ||
db.close() | ||
|
||
|
||
class UntitledTable256(Base): | ||
primary_key_of_table = "primary_key" | ||
unique_fields = ['primary_key', 'int4_value', 'float4_value'] | ||
__tablename__ = 'test_build_myself' | ||
__table_args__ = ( | ||
UniqueConstraint('primary_key', 'int4_value', 'float4_value'), | ||
) | ||
primary_key = Column(Integer, primary_key=True, info={'alias_name': 'primary_key'}, autoincrement=True, | ||
server_default="nextval('test_build_myself_id_seq'::regclass)") | ||
bool_value = Column(Boolean, nullable=False, server_default=text("false")) | ||
# bytea_value = Column(LargeBinary) | ||
char_value = Column(CHAR(10)) | ||
date_value = Column(Date, server_default=text("now()")) | ||
float4_value = Column(Float, nullable=False) | ||
float8_value = Column(Float(53), nullable=False, server_default=text("10.10")) | ||
int2_value = Column(SmallInteger, nullable=False) | ||
int4_value = Column(Integer, nullable=True) | ||
int8_value = Column(BigInteger, server_default=text("99")) | ||
interval_value = Column(INTERVAL) | ||
json_value = Column(JSON) | ||
jsonb_value = Column(JSONB(astext_type=Text())) | ||
numeric_value = Column(Numeric) | ||
text_value = Column(Text) | ||
time_value = Column(Time) | ||
timestamp_value = Column(DateTime) | ||
timestamptz_value = Column(DateTime(True)) | ||
timetz_value = Column(Time(True)) | ||
uuid_value = Column(UUID(as_uuid=True)) | ||
varchar_value = Column(String) | ||
# xml_value = Column(NullType) | ||
array_value = Column(ARRAY(Integer())) | ||
array_str__value = Column(ARRAY(String())) | ||
# box_valaue = Column(NullType) | ||
|
||
|
||
crud_route_child2 = crud_router_builder( | ||
db_model=UntitledTable256, | ||
prefix="/blog_comment", | ||
tags=["blog_comment"], | ||
db_session=get_transaction_session, | ||
crud_methods=[CrudMethods.FIND_ONE] | ||
) | ||
|
||
app = FastAPI() | ||
[app.include_router(i) for i in [crud_route_child2]] | ||
|
||
|
||
@app.get("/", tags=["child"]) | ||
async def root(): | ||
return {"message": "Hello World"} | ||
|
||
|
||
import uvicorn | ||
|
||
uvicorn.run(app, host="0.0.0.0", port=8002, debug=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
from .misc.utils import sqlalchemy_to_pydantic | ||
from .crud_router import crud_router_builder | ||
from .crud_generator import crud_router_builder | ||
from .misc.type import CrudMethods | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
from typing import \ | ||
List, \ | ||
TypeVar, Union, Optional | ||
|
||
from fastapi import \ | ||
APIRouter | ||
from pydantic import \ | ||
BaseModel | ||
from sqlalchemy.sql.schema import Table | ||
|
||
from . import sqlalchemy_to_pydantic | ||
from .generator.common_module_template_generator import CommonModuleTemplateGenerator | ||
from .generator.crud_template_generator import CrudTemplateGenerator | ||
from .misc.crud_model import CRUDModel | ||
from .misc.get_table_name import get_table_name | ||
from .misc.type import CrudMethods, SqlType | ||
from .misc.utils import convert_table_to_model | ||
from .model.common_builder import CommonCodeGen | ||
from .model.crud_builder import CrudCodeGen | ||
|
||
CRUDModelType = TypeVar("CRUDModelType", bound=BaseModel) | ||
CompulsoryQueryModelType = TypeVar("CompulsoryQueryModelType", bound=BaseModel) | ||
OnConflictModelType = TypeVar("OnConflictModelType", bound=BaseModel) | ||
|
||
|
||
def crud_router_builder( | ||
*, | ||
db_model_list: Union[Table, 'DeclarativeBaseModel'], | ||
async_mode: Optional[bool], | ||
sql_type: Optional[SqlType], | ||
crud_methods: Optional[List[CrudMethods]] = None, | ||
exclude_columns: Optional[List[str]] = None, | ||
# foreign_include: Optional[Base] = None | ||
) -> APIRouter: | ||
""" | ||
@param db_model: | ||
The Sqlalchemy Base model/Table you want to use it to build api. | ||
@param async_mode: | ||
As your database connection | ||
@param sql_type: | ||
You sql database type | ||
@param db_session: | ||
The callable variable and return a session generator that will be used to get database connection session for fastapi. | ||
@param crud_methods: | ||
Fastapi Quick CRUD supports a few of crud methods, and they save into the Enum class, | ||
get it by : from fastapi_quickcrud_codegen_codegen import CrudMethods | ||
example: | ||
[CrudMethods.GET_MANY,CrudMethods.ONE] | ||
note: | ||
if there is no primary key in your SQLAlchemy model, it dose not support request with | ||
specific resource, such as GET_ONE, UPDATE_ONE, DELETE_ONE, PATCH_ONE AND POST_REDIRECT_GET | ||
this is because POST_REDIRECT_GET need to redirect to GET_ONE api | ||
@param exclude_columns: | ||
Fastapi Quick CRUD will get all the columns in you table to generate a CRUD router, | ||
it is allow you exclude some columns you dont want it expose to operated by API | ||
note: | ||
if the column in exclude list but is it not nullable or no default_value, it may throw error | ||
when you do insert | ||
@param dependencies: | ||
A variable that will be added to the path operation decorators. | ||
@param crud_models: | ||
You can use the sqlalchemy_to_pydantic() to build your own Pydantic model CRUD set | ||
@param foreign_include: BaseModel | ||
Used to build foreign tree api | ||
@return: | ||
APIRouter for fastapi | ||
""" | ||
model_list = [] | ||
for db_model_info in db_model_list: | ||
|
||
db_model = db_model_info["db_model"] | ||
prefix = db_model_info["prefix"] | ||
tags = db_model_info["tags"] | ||
|
||
table_name = db_model.__name__ | ||
model_name = get_table_name(db_model) | ||
|
||
model_list.append({"model_name": model_name, "file_name": table_name}) | ||
|
||
|
||
db_model, NO_PRIMARY_KEY = convert_table_to_model(db_model) | ||
|
||
# code gen | ||
crud_code_generator = CrudCodeGen(model_name, model_name=table_name, tags=tags, prefix=prefix) | ||
# create a file | ||
crud_template_generator = CrudTemplateGenerator() | ||
|
||
constraints = db_model.__table__.constraints | ||
|
||
common_module_template_generator = CommonModuleTemplateGenerator() | ||
|
||
# type | ||
common_code_builder = CommonCodeGen() | ||
common_code_builder.build_type() | ||
common_code_builder.gen(common_module_template_generator.add_type) | ||
|
||
# module | ||
common_utils_code_builder = CommonCodeGen() | ||
common_utils_code_builder.build_utils() | ||
common_utils_code_builder.gen(common_module_template_generator.add_utils) | ||
|
||
# http_exception | ||
common_http_exception_code_builder = CommonCodeGen() | ||
common_http_exception_code_builder.build_http_exception() | ||
common_http_exception_code_builder.gen(common_module_template_generator.add_http_exception) | ||
|
||
# db | ||
common_db_code_builder = CommonCodeGen() | ||
common_db_code_builder.build_db() | ||
common_db_code_builder.gen(common_module_template_generator.add_db) | ||
|
||
if not crud_methods and NO_PRIMARY_KEY == False: | ||
crud_methods = CrudMethods.get_declarative_model_full_crud_method() | ||
if not crud_methods and NO_PRIMARY_KEY == True: | ||
crud_methods = CrudMethods.get_table_full_crud_method() | ||
|
||
crud_models_builder: CRUDModel = sqlalchemy_to_pydantic | ||
crud_models: CRUDModel = crud_models_builder(db_model=db_model, | ||
constraints=constraints, | ||
crud_methods=crud_methods, | ||
exclude_columns=exclude_columns, | ||
sql_type=sql_type, | ||
exclude_primary_key=NO_PRIMARY_KEY) | ||
|
||
methods_dependencies = crud_models.get_available_request_method() | ||
primary_name = crud_models.PRIMARY_KEY_NAME | ||
if primary_name: | ||
path = '/{' + primary_name + '}' | ||
else: | ||
path = "" | ||
|
||
def find_one_api(): | ||
crud_code_generator.build_find_one_route(async_mode=async_mode, path=path) | ||
|
||
api_register = { | ||
CrudMethods.FIND_ONE.value: find_one_api, | ||
} | ||
for request_method in methods_dependencies: | ||
value_of_dict_crud_model = crud_models.get_model_by_request_method(request_method) | ||
crud_model_of_this_request_methods = value_of_dict_crud_model.keys() | ||
for crud_model_of_this_request_method in crud_model_of_this_request_methods: | ||
api_register[crud_model_of_this_request_method.value]() | ||
crud_code_generator.gen(crud_template_generator) | ||
|
||
# sql session | ||
common_db_session_code_builder = CommonCodeGen() | ||
common_db_session_code_builder.build_db_session(model_list=model_list) | ||
common_db_session_code_builder.gen(common_module_template_generator.add_memory_sql_session) | ||
|
||
# app py | ||
common_app_code_builder = CommonCodeGen() | ||
common_app_code_builder.build_app(model_list=model_list) | ||
common_app_code_builder.gen(common_module_template_generator.add_app) |
Oops, something went wrong.