Skip to content

Commit

Permalink
Use SQLAlchemy defaults in fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Yehor Komarov committed Feb 12, 2024
1 parent 09e8ba3 commit f17d460
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 6 deletions.
6 changes: 4 additions & 2 deletions examples/sqla/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import datetime

from sqlalchemy import (
Boolean,
Column,
Date,
DateTime,
Expand Down Expand Up @@ -51,9 +52,10 @@ class Post(Base):
id = Column(Integer, primary_key=True)
title = Column(String(100), nullable=False)
text = Column(Text, nullable=False)
date = Column(Date)
status = Column(Enum(Status))
date = Column(Date, default=datetime.today)
status = Column(Enum(Status), default=Status.PENDING)
created_at = Column(DateTime, default=datetime.utcnow)
is_public = Column(Boolean, default=True, nullable=False)

publisher_id = Column(Integer, ForeignKey(User.id))
publisher = relationship(User, back_populates="posts")
Expand Down
22 changes: 20 additions & 2 deletions starlette_admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,20 @@ async def _render_create(self, request: Request) -> Response:
request.state.action = RequestAction.CREATE
identity = request.path_params.get("identity")
model = self._find_model_from_identity(identity)
config = {"request": request, "title": model.title(request), "model": model}
dict_obj = self.defaults_to_dict(request, model, RequestAction.CREATE)
config = {
"request": request,
"title": model.title(request),
"model": model,
"obj": dict_obj,
}
if not model.is_accessible(request) or not model.can_create(request):
raise HTTPException(HTTP_403_FORBIDDEN)
if request.method == "GET":
return self.templates.TemplateResponse(model.create_template, config)
form = await request.form()
dict_obj = await self.form_to_dict(request, form, model, RequestAction.CREATE)
form_dict = await self.form_to_dict(request, form, model, RequestAction.CREATE)
dict_obj.update(form_dict)
try:
obj = await model.create(request, dict_obj)
except FormValidationError as exc:
Expand Down Expand Up @@ -494,6 +501,17 @@ async def _render_error(
status_code=exc.status_code,
)

def defaults_to_dict(
self,
request: Request,
model: BaseModelView,
action: RequestAction,
) -> dict[str, Any]:
data = {}
for field in model.get_fields_list(request, action):
data[field.name] = field.default
return data

async def form_to_dict(
self,
request: Request,
Expand Down
16 changes: 15 additions & 1 deletion starlette_admin/contrib/sqla/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
RelationshipProperty,
)
from sqlalchemy.sql.elements import ColumnElement, Label
from sqlalchemy.sql.schema import ScalarElementColumnDefault
from starlette_admin.contrib.sqla.exceptions import NotSupportedColumn
from starlette_admin.contrib.sqla.fields import FileField, ImageField
from starlette_admin.converters import BaseModelConverter, converts
Expand Down Expand Up @@ -130,15 +131,28 @@ def _field_common(
"exclude_from_edit": True,
"exclude_from_create": True,
}

default_value = ""
help_text = column.comment
if column.default:
if isinstance(column.default, ScalarElementColumnDefault):
default_value = column.default.arg
elif help_text:
# We can't evaluate even CallableColumnDefault because it requires execution context
help_text = f"{help_text}. Defaulted to {column.default.arg}"
else:
help_text = f"Defaulted to {column.default.arg}"

return {
"name": name,
"help_text": column.comment,
"help_text": help_text,
"required": (
not column.nullable
and not isinstance(column.type, (Boolean,))
and not column.default
and not column.server_default
),
"default": default_value,
}

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion starlette_admin/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class BaseField:
search_builder_type: datatable columns.searchBuilderType, For more information
[click here](https://datatables.net/reference/option/columns.searchBuilderType)
required: Indicate if the fields is required
default: Default value for the field
exclude_from_list: Control field visibility in list page
exclude_from_detail: Control field visibility in detail page
exclude_from_create: Control field visibility in create page
Expand All @@ -74,6 +75,7 @@ class BaseField:
id: str = ""
search_builder_type: Optional[str] = "default"
required: Optional[bool] = False
default: str = ""
exclude_from_list: Optional[bool] = False
exclude_from_detail: Optional[bool] = False
exclude_from_create: Optional[bool] = False
Expand All @@ -84,7 +86,7 @@ class BaseField:
form_template: str = "forms/input.html"
label_template: str = "forms/_label.html"
display_template: str = "displays/text.html"
error_class = "is-invalid"
error_class: str = "is-invalid"

def __post_init__(self) -> None:
if self.label is None:
Expand Down

0 comments on commit f17d460

Please sign in to comment.