3

I'm learning a new tool called SQLModel, by Sebastian Ramirez (The FastAPI creator).

For basic CRUD operations in SQLModel, the docs teach you it's necessary to set up a model entity like this:

from typing import Optional, List
from sqlmodel import Field, SQLModel, Relationship


class RoomBase(SQLModel):
    name: str
    is_ensuite: bool
    current_occupants: Optional[List[str]]
    house_id: Optional[int] = Field(default=None, foreign_key="house.id")

class Room(RoomBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    house: Optional["House"] = Relationship(back_populates="rooms")

class RoomCreate(RoomBase):
    pass

class RoomRead(RoomBase):
    id: int

class RoomUpdate(SQLModel):
    name: Optional[str] = None
    is_ensuite: Optional[bool] = None
    current_occupants: Optional[List[str]] = None
    house_id: Optional[int] = None

My example above will create a model called Room, which is part of a House. This would have to be repeated for every new model class, meaning I can forget about putting multiple models in the same file.

Lots of code for a little CRUD, right!?

Since it's likely that I will use the same CRUD setup 90% of the time (e.g. I will always want all the fields to be editable on an update, or I will always only need the ID for a read, etc.), it got me wondering whether the above could be abstracted, so that whole file didn't have to be repeated for EVERY SINGLE database entity.

Is it possible in Python to pass in fields and types by means of inheritance or otherwise, such that I would only need to write a generic version of the above code once, rather than having to write it all out for every model?

John Kealy
  • 1,503
  • 1
  • 13
  • 32
  • There's nothing stopping you from having a generic `FetchFromId` request schema in FastAPI - that would give you a request that expects a single `id` field for example. In any case; you can create a base model that you inherit from instead of the generic SQLModel, or you can use mixins to add certain fields (such as id/created_at/updated_at/deleted_at, etc.) to all models that have the mixin. – MatsLindh Oct 03 '21 at 16:26
  • Since you haven't included any of your actual query / service code, it's hard to say exactly how you could generalize your methods; but in general, everything is an object in Python, so you can send in a class to a generic method if the class is the only thing that changes. – MatsLindh Oct 03 '21 at 16:37
  • I share your sentiment John,~but I can't think of any way to abstract this away without it becoming seriously unreadable. Perhaps a code generator would be handy for this? Or a bare minimum version that you could copy-pasta into a new class and do a search-and-replace? Not very satisfying, I know...~ Maybe you can use type() to generate the classes automatically? Would be interesting, gonna play around with it and will revert back! – JarroVGIT Oct 10 '21 at 20:45
  • Well that became tedious very fast, 10/10 would not recommend to create a function that generates classes for you, lot's of edge cases especially with foreign relationships etc. – JarroVGIT Oct 10 '21 at 20:55
  • Well thanks for trying that out @JarroVGIT! A negative result maybe unsatisfying, but it's still very useful. – John Kealy Oct 18 '21 at 11:43
  • @JohnKealy, I'm running into the same thing here. Did you ever "solve" this? – MEMark Feb 01 '22 at 14:25
  • @MEMark, nope, it doesn't seem to be possible :( – John Kealy Feb 01 '22 at 14:45

1 Answers1

1

It appears you are using fastapi. If so, what about fastapi-crudrouter? It did the bulk of the work for me. While googling for the fastapi-crudrouter link, I found another project FastAPIQuickCrud. Just skimming, but it seems to solve the same problem.

Frustrated
  • 772
  • 1
  • 8
  • 16
  • Thanks for the tips, I didn't know about these libraries! I'll definitely check them out. This doesn't answer the specific question though, as neither of these supports SQLModel yet. – John Kealy Mar 22 '22 at 09:32