[HN Gopher] Show HN: Prisma Python - A fully typed ORM for Python
       ___________________________________________________________________
        
       Show HN: Prisma Python - A fully typed ORM for Python
        
       I created this ORM to fill a gap in the Python ecosystem. Due to
       the nature of typing in Python there are no other Python ORMs that
       can provide correct type hints. Prisma Python manages to work
       around this by auto-generating python types.  Aside from static
       type checking, providing type hints means that you will get
       autocomplete suggestions for you which for me is the killer feature
       for this ORM (see the GIF in the README for an example).  It's also
       built on top of Prisma, a next-generation ORM for TypeScript which
       means that the core query building and connection handling has been
       battle tested, getting around a potential concern with adopting a
       new ORM.  Prisma Python also supports PostgreSQL, SQLite, MongoDB,
       MariaDB and more!
        
       Author : probablyrobert
       Score  : 99 points
       Date   : 2022-02-21 14:57 UTC (8 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | hutrdvnj wrote:
       | Is it just me who thinks that plain SQL has won the SQL vs ORM
       | battle? I mean why should I learn how to use X different ORM
       | syntaxes in Y different languages and in addition to that lose
       | full control over the actual query.
        
         | NAHWheatCracker wrote:
         | That's how I've personally felt since I first encountered ORMs
         | a decade ago at a data intensive startup where ORMs were a
         | burden. Yet I keep running into ORMs.
         | 
         | Luckily, they are better implemented at my current job. I still
         | think it gets in the way quite often, but I recognize that it
         | enables some outsized returns on effort. Entire REST APIs can
         | be generated with things like Spring Data. SqlAlchemy can be
         | pretty slick in Python.
         | 
         | I still write raw SQL on my personal projects, but I recognize
         | that I have to spend a lot of time doing that.
        
         | probablyrobert wrote:
         | Use whatever works for you! I personally felt the same until I
         | learned of Prisma. The main benefits for me are type checking
         | and autocomplete.
         | 
         | Autocomplete is the big one as it lessens the learning curve
         | immensely. You no longer have to search through documentation
         | to find a relevant method, you simply have to trigger
         | autocomplete and it'll show you what you can do!
        
         | ckmar wrote:
         | Big fan of queries being written in SQL and yielding pure,
         | properly structured business objects.
         | 
         | An example of this approach is PureORM[0]
         | 
         | [0]https://github.com/craigmichaelmartin/pure-orm
        
       | samwillis wrote:
       | > by auto-generating python types
       | 
       | I'm assuming you are using a code generator? My understanding is
       | that as MyPy is a static analyser there is no way to
       | automatically create types at runtime (which is actually super
       | annoying, particularly for a language as dynamic as Python).
        
         | probablyrobert wrote:
         | You are correct, we use code generation to define the query API
         | types.
        
         | dragonwriter wrote:
         | You can create Python types at runtime, and Python's runtime
         | type checking features predate it's static analyzers.
         | Unfortunately, the additional kinds of objects used for
         | typechecking in the static analyzers (beyond those which are
         | also runtime types) don't work with runtime type checking, nor
         | do static type declarations (even using types that are also
         | runtime types.) (And, obviously, AOT static analyzers can't
         | make use of types that don't exist when they run.)
        
           | samwillis wrote:
           | Exactly, it'a brilliant that they designed a system where the
           | annotations themselves can be dynamic and read at runtime, it
           | has so much potential for interesting things. But MyPy
           | completely hobbles it by preventing all the dynamic
           | potential, even if MyPy had an escape hatch to say ignore
           | anything derived from this type as it's not possible to
           | describe statically that would be better than nothing.
           | 
           | It unfortunately leaves you with a choice of either taking
           | advantage of dynamic annotations or using MyPy, but not both.
           | 
           | And as the sibling comment says, typescript did it so much
           | better.
        
             | BerislavLopac wrote:
             | Python also offers structural typing, which is both dynamic
             | and mypy-compatible.
        
             | probablyrobert wrote:
             | There is actually an escape hatch you can use, although it
             | is a bit clunky:                   ```py         from
             | typing import TYPE_CHECKING              if TYPE_CHECKING:
             | Foo = "expression that mypy can understand"         else:
             | Foo = "dynamic expression"         ```
        
               | samwillis wrote:
               | Somehow in all my searching the other day I missed that,
               | thanks!
        
           | probablyrobert wrote:
           | Yes it is rather unfortunate, Python's typing system doesn't
           | support dynamically creating types like you can in TypeScript
           | :/
        
             | dllthomas wrote:
             | > dynamically creating types
             | 
             | Could you explain what you mean by this? I'm not clear on
             | what typescript behavior this describes.
        
               | joshribakoff wrote:
               | Maybes mapped types are an example?
               | https://www.typescriptlang.org/docs/handbook/2/mapped-
               | types....
        
               | probablyrobert wrote:
               | Yes mapped types is a good example, Pick is also a good
               | example:
               | 
               | https://www.typescriptlang.org/docs/handbook/utility-
               | types.h...
        
               | dllthomas wrote:
               | Ah, I read "dynamic" as implying something at runtime. I
               | agree that these are extremely useful.
        
       | WaitWaitWha wrote:
       | Nowhere is the acronym ORM explained, let alone how it can
       | benefit Python programmers.
        
         | probablyrobert wrote:
         | ORM stands for Object Relational Mapper and is essentially a
         | wrapper over raw SQL queries to provide an easier to use
         | interface.
        
           | tomrod wrote:
           | To add to this answer, an ORM is useful to deploy queries
           | across multiple database vendors (e.g. Oracle, Postgres,
           | SQLite) without re-writing queries. When used correctly it
           | sanitizes SQL as well as supports with typing, as this
           | library assist with.
           | 
           | Though to add to another comment, this is pretty basic in the
           | CRUD and front-end world, and learning how to learn when
           | confronted with unfamiliar information is a very useful life
           | skill.
        
           | WaitWaitWha wrote:
           | Thank you. I heard of ORM, My comment was mostly to enhance
           | the readme.
        
         | occz wrote:
         | Presumably a lot of Python-developers already know what an ORM
         | is and how they can benefit from it, in particular given how
         | long SQLAlchemy has been around.
         | 
         | The rest can presumably hit up their favourite search engine
         | and type in 'ORM', hit 'Search' and learn quite quickly what it
         | is.
        
           | [deleted]
        
       | cabalamat wrote:
       | > Prisma, a next-generation ORM for TypeScript which means that
       | the core query building and connection handling has been battle
       | tested, getting around a potential concern with adopting a new
       | ORM.
       | 
       | Can I suggest you put this at the top of your README.md file, as
       | I had never heard of Prisma before and it would have helped.
       | 
       | > Prisma Python
       | 
       | Is your program called Prisma Python or Prisma Client Python? You
       | seem to be inconsistent with naming.
        
         | probablyrobert wrote:
         | Thanks for the feedback, I will add that.
         | 
         | > Prisma Python
         | 
         | I originally decided to go with Prisma Client Python however I
         | found this to be too verbose and have recently used Prisma
         | Python. I do need to decide on one and stick with that. Thanks
         | for pointing that out
        
       | lytefm wrote:
       | This looks promising. Using prisma both in TypeScript app
       | backends and in Python data pipelines would make switching
       | between the two without much cognitive overhead much easier.
       | 
       | I'll probably oppose introducing a different Python ORM at work
       | until Prisma Python reaches 1.0
        
         | probablyrobert wrote:
         | Thanks :)
         | 
         | Yeah that is understandable, it is being successfully used in
         | production but it is safer to use a more stable ORM for mission
         | critical products.
         | 
         | If you don't want to have to duplicate your model definitions
         | you could write a custom Prisma Generator to generate models
         | for a different Python ORM.
         | 
         | https://prisma-client-py.readthedocs.io/en/stable/reference/...
        
           | lytefm wrote:
           | Nice, sharing the models across Codebases but only doing the
           | migrations on one side is definitely what I'd be looking for.
        
       | leetrout wrote:
       | A comment from a previous Prisma post
       | 
       | > under the hood @prisma/client was spinning up it's own GraphQL
       | server that it would send requests to in order to generate SQL to
       | send to postgres[1]
       | 
       | So is this the same approach you are taking with the HTTP API?
       | 
       | [1] https://news.ycombinator.com/item?id=26889543
        
         | probablyrobert wrote:
         | Yes I am using the same approach however I am working on using
         | native FFI bindings which is what the TypeScript client is
         | using now.
         | 
         | https://github.com/RobertCraigie/prisma-client-py/pull/165
        
           | leetrout wrote:
           | That's great.
           | 
           | I think it makes it such an easy onramp to integrate with
           | something by having HTTP based APIs (or really, gRPC, even)
           | even if it is lower performance compared to native libraries.
           | 
           | Looking forward to trying this.
        
       | mdellavo wrote:
       | fully typed? it used dicts for queryinb - should maybe look at
       | sqlalchemy for comparison
        
         | probablyrobert wrote:
         | Yes it is fully typed because of TypedDicts,
         | https://docs.python.org/3/library/typing.html#typing.TypedDi...
         | 
         | SQLAlchemy on the other hand provides very little (if any) type
         | hints for their query API.
        
       | crucialfelix wrote:
       | I use Prisma typescript daily and love it. It's fast to write
       | complex queries and readable joins. The type hints are well
       | documented and custom generated for your models and
       | relationships.
       | 
       | It would be great if Prisma could support yet more languages!
       | It's a great product.
       | 
       | That said, I would not have two backends sharing the same
       | database, even if one is the master that runs migrations. A
       | component should only have one reason to change.
        
         | probablyrobert wrote:
         | Yeah, sharing the same database between two backends would not
         | be a good idea. That said you do not have to use the TypeScript
         | client at the same time as the Python client, they are
         | independent of each other.
        
       | MaxMoney wrote:
        
       | djstein wrote:
       | @propbablyrobert great job on this! I hope you can get buy-in
       | from the Prisma team for support. This would be great for their
       | moat.
        
         | probablyrobert wrote:
         | Thank you! I hope so too, it would be incredibly helpful.
        
           | sorenbs wrote:
           | This is really cool!
           | 
           | We (Prisma) would love to support this project in any way we
           | can. You can find my email in bio if you are interested in a
           | call :-)
        
       | inshadows wrote:
       | How is this DSL acceptable?                   posts = await
       | client.post.find_many(             where={                 'OR':
       | [                     {'title': {'contains': 'prisma'}},
       | {'content': {'contains': 'prisma'}},                 ]
       | }         )
       | 
       | SQL for comparison:                   ... where title like
       | '%prisma%' or content like '%prisma%'
        
         | dgb23 wrote:
         | Haven't used prisma but I think their query builder type checks
         | (not just the results) which make composing and writing them
         | quite a bit more convenient than the example shows.
        
         | lucas_codes wrote:
         | Now try filtering on a relation!
        
           | probablyrobert wrote:
           | I find the relational API very easy and intuitive to work
           | with. What do you not like about it?
        
             | lucas_codes wrote:
             | So do I - the parent is choosing to compare the most basic
             | example which didn't show the benefits of using an orm over
             | raw sql
        
               | probablyrobert wrote:
               | Ah sorry I misread your comment, I thought you were
               | agreeing and saying that filtering by a relational field
               | is also unacceptable DSL.
               | 
               | Thank you :)
        
         | probablyrobert wrote:
         | How do you suggest to improve it?
        
           | jjice wrote:
           | I think the parent is saying that the SQL example is simpler
           | and easier to read.
        
           | IanCal wrote:
           | Not the original author, but "or" at the start then a list
           | may be better called "any". That (to me) would read more
           | clearly. Particularly if you were passing in the contents,
           | '"or": var' would make me think "var or what?" But '"any":
           | var' seems more obvious.
           | 
           | Has a clear link to the python "any" as well
        
       | joshribakoff wrote:
       | I use Prisma and it's far from perfect but what I love about it
       | is you can introspect a legacy database and generate a Prisma
       | schema, and use the database as the source of truth. You're not
       | tied to a 1:1 relationship between classes and tables, and the
       | correctness of the types do not depend on tediously annotating
       | code correctly. Excited to see more languages supported!
        
       | messo wrote:
       | How does Prisma compare to EdgeDB? Pros and cons? I have limited
       | experiences with DBs and ORMs, but EdgeDB looks very interesting.
        
         | probablyrobert wrote:
         | I am not very familiar with EdgeDB but it is quite different to
         | Prisma. EdgeDB is a database while Prisma is a database client.
         | EdgeDB do provide their own clients to interface with the
         | EdgeDB database but with Prisma you can connect to many
         | different databases, e.g. PostgreSQL, SQLite, MongoDB etc
        
       | bckr wrote:
       | This looks really cool. I might have to try it for my current
       | project.
       | 
       | Question: The Readme says there's room for massive improvement in
       | performance. What's the performance like right now? Any
       | benchmarks?
        
         | probablyrobert wrote:
         | I haven't written benchmarks yet but it is something I am
         | working on. The reason that performance is mentioned in the
         | README is that I haven't spent time on improving it yet.
         | 
         | The biggest blockers in terms of performance are:
         | 
         | - Communication with the internal Rust engine is performed over
         | HTTP instead of FFI.
         | 
         | - You cannot select a subset of fields (every scalar field in a
         | model is selected)
         | 
         | - You cannot skip pydantic validation which while fast is
         | unnecessary in some cases
         | 
         | With that said Prisma Python should be reasonably fast but I
         | would expect it to be on the lower end of the spectrum compared
         | to other Python ORMs. I have ran some load tests locally and
         | creating 500,000 records took about 90 seconds.
        
           | anentropic wrote:
           | What is the Rust engine doing here?
           | 
           | How come GitHub doesn't highlight any Rust code in the
           | project repo?
        
             | probablyrobert wrote:
             | Because Prisma Python currently interfaces with the Rust
             | engine over HTTP (I am looking into changing this) and the
             | Rust engines can be found here:
             | 
             | https://github.com/prisma/prisma-engines
        
               | bb88 wrote:
               | Given the state of PyPi these days and third party
               | packages, I would just prefer to have pure python, even
               | if it is slower.
               | 
               | It means I don't have to worry about things breaking on
               | cross compilation between mac/windows/linux.
        
               | probablyrobert wrote:
               | No need to worry, the current behaviour will be preserved
               | :)
               | 
               | The method used to interface with the internal engine
               | will be able to be configured in the schema:
               | https://www.prisma.io/docs/reference/api-
               | reference/prisma-sc...
        
             | joshribakoff wrote:
             | Prisma uses rust for two main reasons from what I
             | understand. 1) to keep the engine portable and maximize
             | code reuse, to make projects like this Python adapter
             | possible for example 2) under the hood Primsa does not use
             | joins. It does a waterfall of queries (but not n+1), then
             | stitches the data together in memory, which happens in the
             | rust engine for performance reasons
        
       | [deleted]
        
       | klft wrote:
       | Seems to solve the same problem as SQLModel.
       | 
       | From [1]: "SQLModel is a library for interacting with SQL
       | databases from Python code, with Python objects. It is designed
       | to be intuitive, easy to use, highly compatible, and robust.
       | 
       | SQLModel is based on Python type annotations, and powered by
       | Pydantic and SQLAlchemy."
       | 
       | [1] https://sqlmodel.tiangolo.com/
        
         | probablyrobert wrote:
         | Yes it is similar to SQLModel however as SQLModel is based off
         | of SQLAlchemy, the query API is not typed. That is the
         | difference.
        
           | klft wrote:
           | My understanding is that this is exactly what SQLModel adds
           | on top of SQLAlchemy.
           | 
           | statement = select(Hero).where(Hero.age > 32).limit(3)
           | 
           | But I am only halfway through the tutorial so I might be
           | wrong.
           | 
           | HN discussion of SQLModel here [1]
           | 
           | [1] https://news.ycombinator.com/item?id=28294534
        
             | probablyrobert wrote:
             | My understanding of SQLModel is that it brings the benefits
             | of both Pydantic and SQLAlchemy. The benefit of merging
             | Pydantic with SQLAlchemy is that you can then use the
             | database models directly in FastAPI route definitions.
        
           | O5vYtytb wrote:
           | AFAIK SQLAlchemy 2.0 (coming soon?) will use a new system for
           | full typing.
        
             | probablyrobert wrote:
             | Yes I had heard about that but due to the nature of Python
             | typing, they can't actually type the query API properly
             | without falling back to Any, negating the benefits of type
             | checking.
        
           | BozeWolf wrote:
           | The creator of sqlmodel seems to be very busy with fastapi
           | and other projects. Only so much you can do in a day, so
           | progress is slowed down by that.
           | 
           | Prisma looks nice. Is it a solo project mostly? Or is there a
           | company paying for your time to spend on this ORM? To me it
           | is very important that such an important module as an ORM
           | will have support for the lifetime of an application. Or at
           | least when the app is still being developed.
        
             | probablyrobert wrote:
             | Unfortunately this is a solo project, however I am in
             | communication with the Prisma team and the core engine that
             | Prisma Python uses is backed by Prisma.
        
       | melony wrote:
       | Might want to explain in the README that Prisma's core is written
       | in Rust. You don't need to install an entire JS stack to use
       | this.
        
         | probablyrobert wrote:
         | Good point, I'll update it, thanks!
        
         | BozeWolf wrote:
         | Yes! I was looking for this as well.
        
       | PeterCorless wrote:
       | Nice! I opened a feature request for it already and shared with
       | our Python community.
        
         | probablyrobert wrote:
         | Thank you for sharing! It is much appreciated!
        
           | PeterCorless wrote:
           | Also, the ticket I opened was promptly replied to and
           | politely closed. This is actually an issue already open under
           | this ticket:
           | 
           | https://github.com/prisma/prisma/issues/2879
        
       ___________________________________________________________________
       (page generated 2022-02-21 23:01 UTC)