jowilf / starlette-admin

Fast, beautiful and extensible administrative interface framework for Starlette & FastApi applications
https://jowilf.github.io/starlette-admin/
MIT License
546 stars 57 forks source link

Bug: Required foreign key is empty #485

Open mlachowski opened 6 months ago

mlachowski commented 6 months ago

Describe the bug When creating a new entity which has required related model I've got an error.

To Reproduce

class Company(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    name: str 
    facilities: List["Facility"] = Relationship(back_populates="company")

class Facility(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    name: str
    company_id: int = Field(foreign_key="company.id")
    company: Company = Relationship(back_populates="facilities")

During saving the Facility there is an 422 http error and "{'company_id': 'Field required'}" in errors.

Environment (please complete the following information):

hasansezertasan commented 6 months ago

Can you provide ModelViews too?

mlachowski commented 6 months ago

At the beginning I had custom View with list of excluded fields and other settings but then I tried raw approach without any settings and this error still occurs.

from starlette_admin.contrib.sqlmodel import ModelView

facility_view = ModelView(Facility, icon="fa-solid fa-building")

However following code did the trick for me - I'm copying id from selected object into _id field:

arranged_data[f"{field.name}_id"] = arranged_data[field.name].id

    async def _arrange_data(
            self,
            request: Request,
            data: Dict[str, Any],
            is_edit: bool = False,
    ) -> Dict[str, Any]:
        """
        This function will return a new dict with relationships loaded from
        database.
        """
        arranged_data: Dict[str, Any] = {}
        for field in self.get_fields_list(request, request.state.action):
            if isinstance(field, RelationField) and data[field.name] is not None:
                foreign_model = self._find_foreign_model(field.identity)  # type: ignore
                if not field.multiple:
                    arranged_data[field.name] = await foreign_model.find_by_pk(
                        request, data[field.name]
                    )
                    arranged_data[f"{field.name}_id"] = arranged_data[field.name].id
                else:
                    arranged_data[field.name] = await foreign_model.find_by_pks(
                        request, data[field.name]
                    )
            else:
                arranged_data[field.name] = data[field.name]
        return arranged_data

My guess is that pydantic is unhappy about empty company_id but still it seems like should be resolved somehow by sqlmodel / data population of starlette admin.

mlachowski commented 6 months ago

After some more digging it seems like the issue is here:

 self.model.validate(...)

Because pydantic expects to have the _id field during model validation.