Skip to content

Commit e907eec

Browse files
committed
Add parent argument to widget action props. Add wraps to decorator to keep the original function name and docstring. Add pre_generate_models_schema method to ModelAdmin to pre-generate models schema.
1 parent 6bde18a commit e907eec

18 files changed

Lines changed: 1303 additions & 210 deletions

File tree

docs/build.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ def read_cls_docstring(cls):
4444

4545
def get_versions():
4646
return [
47+
{
48+
"version": "0.4.5",
49+
"changes": [
50+
"Add parent argument to widget action props.",
51+
"Add wraps to decorator to keep the original function name and docstring.",
52+
"Add pre_generate_models_schema method to ModelAdmin to pre-generate models schema.",
53+
"Fix examples.",
54+
],
55+
},
4756
{
4857
"version": "0.4.4",
4958
"changes": ["Replace helmet to update title and description.", "Improve frontend coverage."],

docs/index.html

Lines changed: 600 additions & 19 deletions
Large diffs are not rendered by default.

examples/django_djangoorm/orm/models.py

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import uuid
23

34
from django.db import models
@@ -9,6 +10,7 @@
910
WidgetActionChartProps,
1011
WidgetActionFilter,
1112
WidgetActionInputSchema,
13+
WidgetActionParentArgumentProps,
1214
WidgetActionProps,
1315
WidgetActionResponseSchema,
1416
WidgetActionType,
@@ -139,6 +141,17 @@ def upload_file(
139141
# save file to media directory or to s3/filestorage here
140142
return f"/media/{file_name}"
141143

144+
async def pre_generate_models_schema(self) -> None:
145+
def get_options() -> list:
146+
return list(self.model_cls.objects.values_list("username", flat=True))
147+
148+
options = await asyncio.to_thread(get_options)
149+
widget_action_props: WidgetActionProps = self.__class__.sales_action.widget_action_props
150+
for argument in widget_action_props.arguments:
151+
if argument.name == "username":
152+
argument.widget_props["options"] = [{"label": option, "value": option} for option in options]
153+
break
154+
142155
@widget_action(
143156
widget_action_type=WidgetActionType.ChartLine,
144157
widget_action_props=WidgetActionChartProps(
@@ -335,12 +348,60 @@ def sales_pie_chart(self, payload: WidgetActionInputSchema) -> WidgetActionRespo
335348
widget_action_type=WidgetActionType.Action,
336349
widget_action_props=WidgetActionProps(
337350
arguments=[
351+
# Example of using AsyncSelect widget with parentModel
352+
WidgetActionArgumentProps(
353+
name="user_id",
354+
widget_type=WidgetType.AsyncSelect,
355+
widget_props={
356+
"required": True,
357+
"parentModel": "User",
358+
"idField": "id",
359+
"labelFields": ["__str__", "id"],
360+
},
361+
),
362+
# Example of using Select widget with dynamically loaded options
363+
WidgetActionArgumentProps(
364+
name="username",
365+
widget_type=WidgetType.Select,
366+
widget_props={
367+
"required": True,
368+
# dynamically load options from the database in pre_generate_models_schema method
369+
"options": [],
370+
},
371+
),
372+
# Example of using parent argument with filtered children arguments
373+
WidgetActionArgumentProps(
374+
name="type",
375+
widget_type=WidgetType.Select,
376+
widget_props={
377+
"required": True,
378+
"options": [
379+
{"label": "Sales", "value": "sales"},
380+
{"label": "Revenue", "value": "revenue"},
381+
],
382+
},
383+
),
384+
WidgetActionArgumentProps(
385+
name="sales_date",
386+
widget_type=WidgetType.DatePicker,
387+
widget_props={
388+
"required": True,
389+
},
390+
parent_argument=WidgetActionParentArgumentProps(
391+
name="type",
392+
value="sales",
393+
),
394+
),
338395
WidgetActionArgumentProps(
339-
name="x",
396+
name="revenue_date",
340397
widget_type=WidgetType.DatePicker,
341398
widget_props={
342399
"required": True,
343400
},
401+
parent_argument=WidgetActionParentArgumentProps(
402+
name="type",
403+
value="revenue",
404+
),
344405
),
345406
],
346407
),

examples/fastapi_ponyorm/example.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import asyncio
12
import os
23
import uuid
34
from collections.abc import AsyncGenerator
@@ -11,6 +12,7 @@
1112
from fastapi.middleware.cors import CORSMiddleware
1213
from models import BaseEvent, Event, Tournament, User, db
1314
from pony.orm import commit, db_session
15+
from pony.orm import select as pony_select
1416

1517
from fastadmin import (
1618
PonyORMInlineModelAdmin,
@@ -19,6 +21,7 @@
1921
WidgetActionChartProps,
2022
WidgetActionFilter,
2123
WidgetActionInputSchema,
24+
WidgetActionParentArgumentProps,
2225
WidgetActionProps,
2326
WidgetActionResponseSchema,
2427
WidgetActionType,
@@ -91,6 +94,18 @@ def upload_file(
9194
# save file to media directory or to s3/filestorage here
9295
return f"/media/{file_name}"
9396

97+
async def pre_generate_models_schema(self) -> None:
98+
def get_options() -> list:
99+
with db_session:
100+
return [u.username for u in pony_select(u for u in User)]
101+
102+
options = await asyncio.to_thread(get_options)
103+
widget_action_props: WidgetActionProps = self.__class__.sales_action.widget_action_props
104+
for argument in widget_action_props.arguments:
105+
if argument.name == "username":
106+
argument.widget_props["options"] = [{"label": option, "value": option} for option in options]
107+
break
108+
94109
@widget_action(
95110
widget_action_type=WidgetActionType.ChartLine,
96111
widget_action_props=WidgetActionChartProps(
@@ -287,12 +302,60 @@ def sales_pie_chart(self, payload: WidgetActionInputSchema) -> WidgetActionRespo
287302
widget_action_type=WidgetActionType.Action,
288303
widget_action_props=WidgetActionProps(
289304
arguments=[
305+
# Example of using AsyncSelect widget with parentModel
306+
WidgetActionArgumentProps(
307+
name="user_id",
308+
widget_type=WidgetType.AsyncSelect,
309+
widget_props={
310+
"required": True,
311+
"parentModel": "User",
312+
"idField": "id",
313+
"labelFields": ["__str__", "id"],
314+
},
315+
),
316+
# Example of using Select widget with dynamically loaded options
317+
WidgetActionArgumentProps(
318+
name="username",
319+
widget_type=WidgetType.Select,
320+
widget_props={
321+
"required": True,
322+
# dynamically load options from the database in pre_generate_models_schema method
323+
"options": [],
324+
},
325+
),
326+
# Example of using parent argument with filtered children arguments
327+
WidgetActionArgumentProps(
328+
name="type",
329+
widget_type=WidgetType.Select,
330+
widget_props={
331+
"required": True,
332+
"options": [
333+
{"label": "Sales", "value": "sales"},
334+
{"label": "Revenue", "value": "revenue"},
335+
],
336+
},
337+
),
338+
WidgetActionArgumentProps(
339+
name="sales_date",
340+
widget_type=WidgetType.DatePicker,
341+
widget_props={
342+
"required": True,
343+
},
344+
parent_argument=WidgetActionParentArgumentProps(
345+
name="type",
346+
value="sales",
347+
),
348+
),
290349
WidgetActionArgumentProps(
291-
name="x",
350+
name="revenue_date",
292351
widget_type=WidgetType.DatePicker,
293352
widget_props={
294353
"required": True,
295354
},
355+
parent_argument=WidgetActionParentArgumentProps(
356+
name="type",
357+
value="revenue",
358+
),
296359
),
297360
],
298361
),

examples/fastapi_sqlalchemy/example.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
WidgetActionChartProps,
2020
WidgetActionFilter,
2121
WidgetActionInputSchema,
22+
WidgetActionParentArgumentProps,
2223
WidgetActionProps,
2324
WidgetActionResponseSchema,
2425
WidgetActionType,
@@ -93,6 +94,17 @@ async def upload_file(
9394
# save file to media directory or to s3/filestorage here
9495
return f"/media/{file_name}"
9596

97+
async def pre_generate_models_schema(self) -> None:
98+
sessionmaker = self.get_sessionmaker()
99+
async with sessionmaker() as session:
100+
result = await session.execute(select(self.model_cls.username))
101+
options = list(result.scalars().all())
102+
widget_action_props: WidgetActionProps = self.__class__.sales_action.widget_action_props
103+
for argument in widget_action_props.arguments:
104+
if argument.name == "username":
105+
argument.widget_props["options"] = [{"label": option, "value": option} for option in options]
106+
break
107+
96108
@widget_action(
97109
widget_action_type=WidgetActionType.ChartLine,
98110
widget_action_props=WidgetActionChartProps(
@@ -289,12 +301,60 @@ async def sales_pie_chart(self, payload: WidgetActionInputSchema) -> WidgetActio
289301
widget_action_type=WidgetActionType.Action,
290302
widget_action_props=WidgetActionProps(
291303
arguments=[
304+
# Example of using AsyncSelect widget with parentModel
305+
WidgetActionArgumentProps(
306+
name="user_id",
307+
widget_type=WidgetType.AsyncSelect,
308+
widget_props={
309+
"required": True,
310+
"parentModel": "User",
311+
"idField": "id",
312+
"labelFields": ["__str__", "id"],
313+
},
314+
),
315+
# Example of using Select widget with dynamically loaded options
316+
WidgetActionArgumentProps(
317+
name="username",
318+
widget_type=WidgetType.Select,
319+
widget_props={
320+
"required": True,
321+
# dynamically load options from the database in pre_generate_models_schema method
322+
"options": [],
323+
},
324+
),
325+
# Example of using parent argument with filtered children arguments
326+
WidgetActionArgumentProps(
327+
name="type",
328+
widget_type=WidgetType.Select,
329+
widget_props={
330+
"required": True,
331+
"options": [
332+
{"label": "Sales", "value": "sales"},
333+
{"label": "Revenue", "value": "revenue"},
334+
],
335+
},
336+
),
337+
WidgetActionArgumentProps(
338+
name="sales_date",
339+
widget_type=WidgetType.DatePicker,
340+
widget_props={
341+
"required": True,
342+
},
343+
parent_argument=WidgetActionParentArgumentProps(
344+
name="type",
345+
value="sales",
346+
),
347+
),
292348
WidgetActionArgumentProps(
293-
name="x",
349+
name="revenue_date",
294350
widget_type=WidgetType.DatePicker,
295351
widget_props={
296352
"required": True,
297353
},
354+
parent_argument=WidgetActionParentArgumentProps(
355+
name="type",
356+
value="revenue",
357+
),
298358
),
299359
],
300360
),

examples/fastapi_tortoiseorm/example.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
WidgetActionChartProps,
2020
WidgetActionFilter,
2121
WidgetActionInputSchema,
22+
WidgetActionParentArgumentProps,
2223
WidgetActionProps,
2324
WidgetActionResponseSchema,
2425
WidgetActionType,
@@ -105,6 +106,16 @@ async def upload_file(
105106
# save file to media directory or to s3/filestorage here
106107
return f"/media/{file_name}"
107108

109+
async def pre_generate_models_schema(self) -> None:
110+
model_cls: User = self.model_cls
111+
options = await model_cls.all().values_list("username", flat=True)
112+
# inject options to the class
113+
widget_action_props: WidgetActionProps = self.__class__.sales_action.widget_action_props
114+
for argument in widget_action_props.arguments:
115+
if argument.name == "username":
116+
argument.widget_props["options"] = [{"label": option, "value": option} for option in options]
117+
break
118+
108119
@widget_action(
109120
widget_action_type=WidgetActionType.ChartLine,
110121
widget_action_props=WidgetActionChartProps(
@@ -317,12 +328,60 @@ async def sales_pie_chart(self, payload: WidgetActionInputSchema) -> WidgetActio
317328
widget_action_type=WidgetActionType.Action,
318329
widget_action_props=WidgetActionProps(
319330
arguments=[
331+
# Example of using AsyncSelect widget with parentModel
332+
WidgetActionArgumentProps(
333+
name="user_id",
334+
widget_type=WidgetType.AsyncSelect,
335+
widget_props={
336+
"required": True,
337+
"parentModel": "User",
338+
"idField": "id",
339+
"labelFields": ["__str__", "id"],
340+
},
341+
),
342+
# Example of using Select widget with dynamically loaded options
343+
WidgetActionArgumentProps(
344+
name="username",
345+
widget_type=WidgetType.Select,
346+
widget_props={
347+
"required": True,
348+
# dynamically load options from the database in pre_generate_models_schema method
349+
"options": [],
350+
},
351+
),
352+
# Example of using parent argument with filtered children arguments
353+
WidgetActionArgumentProps(
354+
name="type",
355+
widget_type=WidgetType.Select,
356+
widget_props={
357+
"required": True,
358+
"options": [
359+
{"label": "Sales", "value": "sales"},
360+
{"label": "Revenue", "value": "revenue"},
361+
],
362+
},
363+
),
364+
WidgetActionArgumentProps(
365+
name="sales_date",
366+
widget_type=WidgetType.DatePicker,
367+
widget_props={
368+
"required": True,
369+
},
370+
parent_argument=WidgetActionParentArgumentProps(
371+
name="type",
372+
value="sales",
373+
),
374+
),
320375
WidgetActionArgumentProps(
321-
name="x",
376+
name="revenue_date",
322377
widget_type=WidgetType.DatePicker,
323378
widget_props={
324379
"required": True,
325380
},
381+
parent_argument=WidgetActionParentArgumentProps(
382+
name="type",
383+
value="revenue",
384+
),
326385
),
327386
],
328387
),

0 commit comments

Comments
 (0)