Skip to content

Commit 9e42cc4

Browse files
committed
feat: New feature, update_fields support PropertyField.
1 parent 6e75f73 commit 9e42cc4

2 files changed

Lines changed: 53 additions & 8 deletions

File tree

fastapi_amis_admin/crud/_sqlmodel.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from fastapi.encoders import DictIntStrAny, SetIntStr
2020
from pydantic import Extra, Json
2121
from pydantic.fields import ModelField
22+
from pydantic.utils import ValueItems
2223
from sqlalchemy import Column, Table, delete, func, insert
2324
from sqlalchemy.future import select
2425
from sqlalchemy.orm import InstrumentedAttribute, Session
@@ -313,14 +314,18 @@ def _create_schema_read(self) -> Type[SchemaReadT]:
313314
def _create_schema_update(self) -> Type[SchemaUpdateT]:
314315
if self.schema_update:
315316
return self.schema_update
316-
if not self.readonly_fields and not self.update_fields:
317-
return super(SQLModelCrud, self)._create_schema_update()
318-
self.update_fields = self.parser.filter_insfield(self.update_fields) or self.schema_model.__fields__.values()
317+
self.update_fields = (
318+
self.parser.filter_insfield(self.update_fields, save_class=(ModelField,)) or self.schema_model.__fields__.values()
319+
)
319320
modelfields = [self.parser.get_modelfield(ins, deepcopy=True) for ins in self.update_fields]
320-
readonly_fields = {
321-
self.parser.get_modelfield(ins, deepcopy=False).name for ins in self.parser.filter_insfield(self.readonly_fields)
322-
} | {self.pk_name}
323-
modelfields = [field for field in modelfields if field.name not in readonly_fields]
321+
if self.update_exclude is None: # deprecated in version 0.4.0
322+
exclude = {self.pk_name} | {
323+
self.parser.get_modelfield(ins, deepcopy=False).name
324+
for ins in self.parser.filter_insfield(self.readonly_fields) # readonly fields, deprecated
325+
}
326+
else:
327+
exclude = {k for k, v in ValueItems.merge(self.update_exclude, {}).items() if not isinstance(v, (dict, list, set))}
328+
modelfields = [field for field in modelfields if field.name not in exclude]
324329
return schema_create_by_modelfield(f"{self.schema_name_prefix}Update", modelfields, set_none=True)
325330

326331
def _create_schema_create(self) -> Type[SchemaCreateT]:

tests/test_crud/test_SQLModelCrud_fields.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from fastapi_amis_admin.crud.parser import LabelField, PropertyField
88
from fastapi_amis_admin.models import Field
99
from tests.conftest import async_db as db
10-
from tests.models import Article, Category, User
10+
from tests.models import Article, ArticleContent, Category, User
1111

1212

1313
async def test_pk_name(app: FastAPI, async_client: AsyncClient, fake_users):
@@ -302,3 +302,43 @@ class ArticleCrud(SQLModelCrud):
302302
assert items["category"]["name"] == "Category_1"
303303
assert "content_text" in items
304304
# assert items["user"]["username"] == "User_1"
305+
306+
307+
async def test_update_fields_relationship(app: FastAPI, async_client: AsyncClient, fake_articles, async_session):
308+
class ArticleCrud(SQLModelCrud):
309+
router_prefix = "/article"
310+
update_exclude = {"content": {"id"}}
311+
update_fields = [
312+
Article.description,
313+
PropertyField(name="content", type_=ArticleContent), # Relationship attribute
314+
]
315+
316+
ins = ArticleCrud(Article, db.engine).register_crud()
317+
318+
app.include_router(ins.router)
319+
320+
# test schemas
321+
assert "id" not in ins.schema_update.__fields__
322+
assert "title" not in ins.schema_update.__fields__
323+
assert "description" in ins.schema_update.__fields__
324+
assert "content" in ins.schema_update.__fields__
325+
326+
# test api
327+
res = await async_client.put(
328+
"/article/item/1",
329+
json={
330+
"title": "new_title",
331+
"description": "new_description",
332+
"content": {
333+
"id": 22, # will be ignored by `update_exclude`
334+
"content": "new_content",
335+
},
336+
},
337+
)
338+
assert res.json()["data"] == 1
339+
article = await async_session.get(Article, 1)
340+
assert article.title != "new_title"
341+
assert article.description == "new_description"
342+
343+
content = await async_session.get(ArticleContent, 1)
344+
assert content.content == "new_content"

0 commit comments

Comments
 (0)