Love your work. I wanted what it does in like 2007... always felt like extending HTML more was the way it should have gone, instead of the JS hell we spent over a decade in instead. Your work makes me feel like I'm not an idiot, and that my ideas were somewhat valid!
Thank you so much ! Great work with htmx, I'm a fan. This project is the culmination of a lot of concepts I like from many different stacks coming together.
A colleague built an internal app on flask/htmx/sqlalchemy and had excellent results but couldn't get approval to open source it. Excited to see your work!
Language
Python vs. Raku
Web framework
Flask vs. Cro
ORM
?? vs. Red
Components
both
HTML
template vs. functional
CSS
DaisyUI/Tailwind/Bootstrap vs. Pico
So, I think you have a much wider audience with a very popular set of options - for comparison HARC stack makes more "out there" choices which I hope will appeal to the small group of folks who want to try a new way to go - like the idea of functional code for HTML (think elmlang on the server side) and are a bit allergic to Tailwind denormalization.
Why sqlorm and not sqlalchemy? I've been out of the Python dev space for a long time (maybe it shows), but I thought everyone used SQLAlchemy, and I never heard off sqlorm.
sqlorm is a new orm developed as part of hyperflask.
I use sqlalchemy daily, it's an amazing library, but I wanted something more lightweight and straightforward for this project. I find the unit of work pattern cumbersome
SQLAlchemy Core isn't an ORM, it's just a very good query generator.
Although nobody seems to use the term ORM correctly any more so it's entirely possible that neither is peewee or sqlorm.
The story behind why ORM is nowadays no longer used correctly is kind of funny:
1. Query generator sounds primitive, like cavemen banging rocks together. Software engineers are scared of primitive technologies because it makes their CVs look bad.
2. Actual ORMs attempt to present a relational database as if it was a graph or document database. This fundamentally requires a translation which cannot be made performant automatically and often requires very careful work to make performant (which is a massive source of abstraction leaks in real ORMs). People don't realise the performance hit until they've written a chunk of their application and start getting users.
3. Once enough people encountered this problem, they decided to "improve" ORMs by writing new "ORMs" which "avoid" this problem by just not actually doing any of the heavy mapping. i.e. They're the re-invention of a query generator.
I don’t know if I buy that. Object-relational mapping can in principle be a broad spectrum of possibilities. SQLAlchemy (the original, not Core) is an ORM that still exposes some of the underlying relational aspects.
It is still basically a query generator, just with the helpful step of converting selected tuples into objects, and tracking changes to those objects. This means that it is often possible to solve ORM-related performance issues without too much work.
It’s been a long time since I worked with SQLAlchemy though (or even touched Python), so my memory or knowledge of the current ecosystem might be off.
Cool, the term "object-relational mapping" does indeed sound broad, as if it could be applied to merely the act of mapping tuples into something more structured, but that doesn't matter. It has a definition.
If people started using the term "data serialization" to mean taking parallel data and making it serial (for an English speaker, a perfectly reasonable meaning) would you say that this is what data serialization also was?
The term object-relational mapping refers to the very specific concept of taking relational databases and letting you access them as if they were a database of objects. Specifically, in which you had objects which held one-to-one or one-to-many or many-to-one references to other objects etc. This is a graph. The object-relational mismatch deals with the fact that relational databases and graphs are fundamentally different such that there isn't a well defined way to represent all kinds of one as the other and vice versa. Moreover, there is a performance penalty to attempting to pretend that your relational database is a graph database, and querying the relational data in ways which would make sense for a graph database.
In the case of SQLAlchemy ORM (not Core) when I worked with it back in 2018 now you could select all users from a table, or all orders. But if you wanted to select the most recent order for each user, it's much harder and requires breaking more abstractions than if you were to do it using a query generator. This is because SQLAlchemy ORM expected you to represent your data as a graph:
If you _just_ use the ORM you would write something like:
users = session.query(User).all()
most_recent_orders = []
for user in users:
if user.orders:
most_recent = max(user.orders, key=lambda o: o.created_at)
most_recent_orders.append((user, most_recent))
This is the n+1 query problem, and the performance would tank.
To avoid this, you have a few options, the simplest seems to be to add a
virtual fiend to your User object which holds the most recent order:
All this and you don't get users with their corresponding order contained within, you get an abstraction leaking sequence of tuples and their corresponding orders.
This is just one single mildly non-trivial example. All this extra boilerplate just to get performance.
I’ve written a few ORMs and you have the same performance executing a select and getting rows back and translating them into objects than you do my ORMs. It’s literally the same. Are you going to return back a Row* from your function? No. You’re going to return an object or an array. Building that from an array of rows is no different than an ORM mapping those rows for you using instructions on what field goes where.
Just like you do in your function to build an object. “Oh but it’s at compile time!” You’ll shout. So are mine. CodeGen exists. The real issue you experience is that a certain style of ORMs confuse you. Unit of work or ActiceRecord pattern style ORMs can literally be codegen’ed into your binary to make mapping as fast as new() {}.
ORMs provide you with objects and relationships. Some of them even do validation should you choose. It’s about correctness and not going Wild West on your database with no regard to schema or normalization. If you’re properly normalized, you’ll be thankful for ORMs saving you from the JOIN hell you so desperately hang on to.
You seem to be disagreeing on what the term ORM means and using the new, not very useful, and very far from the original definition. I do recommend you look at the literature of the time when ORMs and the "Object Relational Mismatch" became a hot topic and look at how ORMs worked and how people used them. Because you would be surprised to find that it's nothing like what you describe.
I didn't say you didn't want a query generator, or to have some way of mapping things automatically to native types so you are not effectively dealing with native database types.
What you don't want, which is what object relational mapping means / was designed to solve, is to force your relational data into being represented as a graph of objects when you operate on that data within your application.
To finalize:
ORMs use query generation, but they are not the originators or the only source of query generators
ORMs often provide additional validation, but they are not the originators or the only source of validation
ORMs by their definition map your SQL data to your native types, but fundamentally they do it in a way which cannot be made performant at scale. You can map things to your native types in a way which is performant but this would definitionally not be an ORM
I'm surprised an ORM is in scope for this project, but in any case I would have thought a framework would make unit of work not cumbersome. For example you could just tie the unit of work to the request cycle.
It's not heavy. The abstrations allow for consistent query compositions from reusable expressions. Demonstrate how you do conditional query building for everyone to witness the way.
This looks awesome. I’m building an app in pure Flask using Tailwind + DaisyUI + Bootstrap Icons which looks to be the exact stack you’ve gone with. Though admittedly I am just writing raw JavaScript without a JS framework.
I‘m doing this as well, and I really like the simplicity and „no build steps“ approach.
Everything is rendered server-side, with sprinkles of modern JS where needed or useful, powered by some JSON-serving routes in the same flask app. A CSS framework on top to make it look good.
Basically more or less how we built web apps 15 years ago. :-)
I’ve been looking for something like this but backend agnostic (library rather than a framework) because we’re already about a million lines deep into a Django project. Anyway to lift this up into something that I can just mount into a Django app?
I knew of unpoly but didn't know about alpine-ajax. My choice was set on htmx since the beginning. I feel it's a more mature solution with more flexibility.
The problem really comes from the capacity for a coop to raise funds. A coop is a non-capitalistic company so very few investors are interested.
There is no valuation other than amount of shares x value of share. Voting power is not dependent on share (1 person = 1 vote).
Making an exit is thus not a possibility. Investors have little interest to finance a traditional company that would only yield dividends (if it does, which is not mandatory).
The problem of speed is also often cited: democracy takes more time. While I think that's true, I haven't felt that was a limiting factor. Financing is a much bigger one.
Happy to see this list here on which our coop startup appears! (https://kantree.io) It is very challenging to raise funds as a coop indeed but we have managed to do it on small amounts (a few hundreds of thousands). We are one of the few product based coop and not service based.
France (where we are based) is actually a very interesting market for coops. There is a very good legal framework around coops and a growing percentage of them. There's even an investment fund of tech coop as of recently! https://coopventure.fr/
I think coops are a great ownership model for the future and for a more responsible economy. It is too bad that there is still very little support and recognition around it.
From what I understand, there is no limitation regarding the type of company when it comes to the French Tech Visa (as long as it qualifies on the other requirements which focus more on the innovation aspect). (my coop would be eligible for example)
Starting a coop is like starting any other kind of company so same rules apply. (You must be at least 2 people of course!)
Not necessarily (the coop I work for is one though). There are 3 possible types of coops in France: SCOP, SCIC and CAE. To make it short:
- SCOP: adheres to the international definition of coops
- CAE: a coop of freelancers. You are an employee of the structure (with a work contract) but you have to bring in your own revenue to pay your salary. A percentage of your revenue goes towards the coop budget for shared services (accounting, hr, ...) and cash reserve
- SCIC: a multi-party coop. A good use case would be platforms where you could have multiple groups of stakeholders: employees of the platform, users and service providers. (eg. with a Uber like coop: employees, users and drivers could all be separate groups of owners). Each group has a voting percentage. No group can have more than 50% ownership.
I see great potential for the 2 other types of coops that France has in the context of tech. I do not know of any equivalent elsewhere (but would be happy to learn it exists!)
I'm one of the co-founder of Digicoop, a worker cooperative based in France. We develop Kantree, a work management platform as SaaS or on-prem solution.
We are a small team, all employees are associates and we are remote first. New employees must become associate after 1 year.
The hardest part for us is funding as no investors are interested in worker coops.
We had quite a few hurdles on the way and the startup scene mostly rejects outliers like us as we have no true funding story to tell. But we really believe in this model and fight hard to keep going.
We have written about our company on kantree's blog if you want more details.
Are you a developer? I'm also wanting to start a coop. Something with maybe our own SaaS that has MRR but also does agency work for companies. If you or anyone else seeing this wants to team up let me know.