mirror of
https://github.com/fastapi-admin/fastapi-admin.git
synced 2026-03-13 10:32:25 +08:00
Add password auto hash.
`Field` description as help text for form.
This commit is contained in:
@@ -4,6 +4,12 @@ ChangeLog
|
||||
|
||||
0.2
|
||||
===
|
||||
|
||||
0.2.8
|
||||
-----
|
||||
- Add password auto hash.
|
||||
- `Field` description as help text for form.
|
||||
|
||||
0.2.7
|
||||
-----
|
||||
- Add custom login_view.
|
||||
|
||||
@@ -210,10 +210,10 @@ class Status(EnumMixin, IntEnum):
|
||||
}
|
||||
```
|
||||
|
||||
### Verbose Name
|
||||
### Help Text
|
||||
|
||||
FastAPI-Admin will auto read `description` defined in tortoise-orm model
|
||||
`Field` and display in front.
|
||||
`Field` and display in front with form help text.
|
||||
|
||||
### ForeignKeyField Support
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ async def start_up():
|
||||
locale_switcher=True,
|
||||
theme_switcher=True,
|
||||
),
|
||||
login_view="examples.routes.login",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@ from passlib.context import CryptContext
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
|
||||
async def handle_m2m_fields_create_or_update(body, m2m_fields, model, create=True, pk=None):
|
||||
async def handle_m2m_fields_create_or_update(
|
||||
body, m2m_fields, model, user_model, create=True, pk=None
|
||||
):
|
||||
"""
|
||||
handle m2m update or create
|
||||
:param user_model:
|
||||
:param body:
|
||||
:param m2m_fields:
|
||||
:param model:
|
||||
@@ -17,6 +20,8 @@ async def handle_m2m_fields_create_or_update(body, m2m_fields, model, create=Tru
|
||||
:return:
|
||||
"""
|
||||
copy_body = deepcopy(body)
|
||||
if model == user_model:
|
||||
copy_body["password"] = pwd_context.hash(copy_body.pop("password"))
|
||||
m2m_body = {}
|
||||
for k, v in body.items():
|
||||
if k in m2m_fields:
|
||||
|
||||
@@ -113,11 +113,7 @@ class AdminApp(FastAPI):
|
||||
permission_menus = [
|
||||
Menu(name="Auth", title=True),
|
||||
Menu(
|
||||
name="User",
|
||||
url="/rest/User",
|
||||
icon="fa fa-user",
|
||||
exclude=("password",),
|
||||
search_fields=("username",),
|
||||
name="User", url="/rest/User", icon="fa fa-user", search_fields=("username",),
|
||||
),
|
||||
Menu(name="Role", url="/rest/Role", icon="fa fa-group", actions={"delete": False}),
|
||||
Menu(
|
||||
@@ -221,15 +217,15 @@ class AdminApp(FastAPI):
|
||||
fields = {}
|
||||
pk = name = pk_field.get("name")
|
||||
if not exclude_pk and not self._exclude_field(resource, name):
|
||||
fields = {
|
||||
name: Field(
|
||||
label=pk_field.get("name").title(),
|
||||
required=True,
|
||||
type=self._get_field_type(name, pk_field.get("field_type").__name__, menu),
|
||||
sortable=name in sort_fields,
|
||||
**menu.attrs.get(name) or {},
|
||||
)
|
||||
}
|
||||
field = Field(
|
||||
label=pk_field.get("name").title(),
|
||||
required=True,
|
||||
type=self._get_field_type(name, pk_field.get("field_type").__name__, menu),
|
||||
sortable=name in sort_fields,
|
||||
description=pk_field.get("description"),
|
||||
)
|
||||
field = field.copy(update=menu.attrs.get(name) or {})
|
||||
fields = {name: field}
|
||||
if not exclude_actions and menu.actions:
|
||||
fields["_actions"] = menu.actions
|
||||
|
||||
@@ -246,7 +242,7 @@ class AdminApp(FastAPI):
|
||||
for k, v in model._meta.fields_map[name].enum_type.choices().items():
|
||||
options.append({"text": v, "value": k})
|
||||
|
||||
label = data_field.get("description") or data_field.get("name").title()
|
||||
label = data_field.get("name").title()
|
||||
field = Field(
|
||||
label=label,
|
||||
required=not data_field.get("nullable"),
|
||||
@@ -254,8 +250,9 @@ class AdminApp(FastAPI):
|
||||
options=options,
|
||||
sortable=name in sort_fields,
|
||||
disabled=readonly,
|
||||
**menu.attrs.get(name) or {},
|
||||
description=data_field.get("description"),
|
||||
)
|
||||
field = field.copy(update=menu.attrs.get(name) or {})
|
||||
fields[name] = field
|
||||
if name in search_fields:
|
||||
search_fields_ret[name] = field
|
||||
@@ -267,7 +264,7 @@ class AdminApp(FastAPI):
|
||||
fk_model_class = fk_field.get("python_type")
|
||||
objs = await fk_model_class.all()
|
||||
raw_field = fk_field.get("raw_field")
|
||||
label = fk_field.get("description") or name.title()
|
||||
label = name.title()
|
||||
options = list(map(lambda x: {"text": str(x), "value": x.pk}, objs))
|
||||
field = Field(
|
||||
label=label,
|
||||
@@ -275,8 +272,9 @@ class AdminApp(FastAPI):
|
||||
type="select",
|
||||
options=options,
|
||||
sortable=name in sort_fields,
|
||||
**menu.attrs.get(name) or {},
|
||||
description=fk_field.get("description"),
|
||||
)
|
||||
field = field.copy(update=menu.attrs.get(name) or {})
|
||||
fields[raw_field] = field
|
||||
if name in search_fields:
|
||||
search_fields_ret[raw_field] = field
|
||||
@@ -284,7 +282,7 @@ class AdminApp(FastAPI):
|
||||
for m2m_field in m2m_fields:
|
||||
name = m2m_field.get("name")
|
||||
if not self._exclude_field(resource, name):
|
||||
label = m2m_field.get("description") or name.title()
|
||||
label = name.title()
|
||||
m2m_model_class = m2m_field.get("python_type")
|
||||
objs = await m2m_model_class.all()
|
||||
options = list(map(lambda x: {"text": str(x), "value": x.pk}, objs))
|
||||
@@ -293,6 +291,7 @@ class AdminApp(FastAPI):
|
||||
type="tree",
|
||||
options=options,
|
||||
multiple=True,
|
||||
description=m2m_field.get("description"),
|
||||
**menu.attrs.get(name) or {},
|
||||
)
|
||||
return pk, fields, search_fields_ret
|
||||
|
||||
@@ -5,7 +5,7 @@ from fastapi_admin import enums
|
||||
|
||||
class User(Model):
|
||||
username = fields.CharField(max_length=20, unique=True)
|
||||
password = fields.CharField(max_length=200)
|
||||
password = fields.CharField(max_length=200, description="Will auto hash with raw password")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -15,7 +15,7 @@ class Permission(Model):
|
||||
label = fields.CharField(max_length=50)
|
||||
model = fields.CharField(max_length=50)
|
||||
action: enums.PermissionAction = fields.IntEnumField(
|
||||
enums.PermissionAction, default=enums.PermissionAction.read, description="Permission Action"
|
||||
enums.PermissionAction, default=enums.PermissionAction.read
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
@@ -120,7 +120,9 @@ async def update_one(id: int, parsed=Depends(parse_body), model=Depends(get_mode
|
||||
body, resource_fields = parsed
|
||||
m2m_fields = model._meta.m2m_fields
|
||||
try:
|
||||
obj = await handle_m2m_fields_create_or_update(body, m2m_fields, model, False, id)
|
||||
obj = await handle_m2m_fields_create_or_update(
|
||||
body, m2m_fields, model, app.user_model, False, id
|
||||
)
|
||||
except IntegrityError as e:
|
||||
return UJSONResponse(
|
||||
status_code=HTTP_409_CONFLICT, content=dict(message=f"Update Error,{e}")
|
||||
@@ -135,7 +137,7 @@ async def create_one(parsed=Depends(parse_body), model=Depends(get_model)):
|
||||
m2m_fields = model._meta.m2m_fields
|
||||
creator = pydantic_model_creator(model, include=resource_fields, exclude=m2m_fields)
|
||||
try:
|
||||
obj = await handle_m2m_fields_create_or_update(body, m2m_fields, model)
|
||||
obj = await handle_m2m_fields_create_or_update(body, m2m_fields, model, app.user_model)
|
||||
except IntegrityError as e:
|
||||
return UJSONResponse(
|
||||
status_code=HTTP_409_CONFLICT, content=dict(message=f"Create Error,{e}")
|
||||
|
||||
Reference in New Issue
Block a user