starlette-admin
starlette-admin copied to clipboard
Enhancement: custom relation field
Is your feature request related to a problem? Please describe. I want to make custom relation field so i can set custom render_function_key and custom display template.
Describe the solution you'd like
A clear and concise description of what you want to happen.
Something like this in list:
and something like this in detail:
Describe alternatives you've considered If i trying to use StringField as base class:
@dataclass
class NotesField(StringField):
rows: int = 6
render_function_key: str = "notes"
class_: str = "field-textarea form-control"
form_template: str = "forms/textarea.html"
display_template: str = "displays/note.html"
exclude_from_create: Optional[bool] = True
exclude_from_edit: Optional[bool] = True
exclude_from_list: Optional[bool] = False
class ClientView(MyModelView):
fields = [
NotesField("notes"),
Client.notes,
]
class DesktView(MyModelView):
fields = [
Desk.clients,
]
It leads to this api response: GET: http://127.0.0.1:8000/admin/api/desk?skip=0&limit=20&order_by=id%20asc: Reponse:
{
"items": [
{
"id": 1,
"client": [
{
"id": 15,
"notes": "[Note(content='test', client_id=15, id=4,), Note(content='teaa', client_id=15, id=6)]",
"_repr": "15",
"_detail_url": "http://127.0.0.1:8000/admin/client/detail/15",
"_edit_url": "http://127.0.0.1:8000/admin/client/edit/15"
}
],
As you can see it resolving "notes" field (relation of a relation) and showing it like text (serializing func thinks it string field). This is problem because it overloads backend by resolving a lot of related records. And it might be not a problem if there is no a lot of relations, but if there a long chain of relations it starting resolving literally all databse which leads to crash of worker and even cycling of resolving records which leads to endless fetching to the database (and blocking it) until worker crashing and restarts by itself.
I found out that i can change "serialize" function to exclude this field:
@dataclass
class CustomRelationField(StringField):
rows: int = 6
async def serialize(
self,
obj: Any,
request: Request,
action: RequestAction,
include_relationships: bool = True,
include_relationships2: bool = True,
include_relationships3: bool = True,
include_select2: bool = False,
) -> Dict[str, Any]:
...
elif not isinstance(field, RelationField):
+ if isinstance(field, CustomRelationField):
+ continue
...
but this method is not allowing me to use relations information in detail view.
This method leads to overloading database in detail view:
async def serialize(
self,
obj: Any,
request: Request,
action: RequestAction,
include_relationships: bool = True,
include_relationships2: bool = True,
include_relationships3: bool = True,
include_select2: bool = False,
) -> Dict[str, Any]:
...
elif not isinstance(field, RelationField):
+ if isinstance(field, CustomRelationField) and action == RequestAction.LIST:
+ continue
...
How i can change serialize def so it stop to resolving in some moment? Or it will be even better if there is another more correct way to customize relation field display?
Somehow this saves the optimization and at the same moment returns all required data:
@dataclass
class CustomRelationField(StringField):
async def serialize_value(
self, request: Request, value: Any, action: RequestAction
) -> Any:
if action == RequestAction.DETAIL:
return '123'
I don't understand how it all works. In some cases it actually didt resolve some needed fields, in some it resolves exactly as many dates as needed.
I found thys syntax in admin demo source code:
class Post(SQLModel, table=True):
id: Optional[int] = Field(primary_key=True)
async def __admin_select2_repr__(self, request: Request) -> str:
template_str = (
"<span><strong>Title: </strong>{{obj.title}}, <strong>Publish by:"
" </strong>{{obj.publisher.full_name}}</span>"
)
return Template(template_str, autoescape=True).render(obj=self)
I belive this can help to change list view. Finally, it remains to understand how to change relation field template in the detail view.
Is there any way to set representation html template for detail view also?
I made _detail_repr If anyone need this I can provide modified code
You can simply override the detail template for this specific field. You can find more information on https://jowilf.github.io/starlette-admin/advanced/custom-field/?h=custom#detail-page
Also, I noticed that you are currently using an outdated version of starlette-admin. I recommend upgrading to the latest version.
class ClientView(MyModelView): fields = [ NotesField("notes"), Client.notes, ]
The field names must be unique per view