Earlier quoted context omitted.
Another great option in Java is jOOQ, which lets you write type-safe and potentially composable queries such as: context .update(User.USER) .set(User.USER.NAME, userName) .where(User.USER.ID.eq(userId)) .execute()
jOOQ and its DSL is good, however IMO it's more readable using raw SQL (by using `context.fetchInto` and its variants) than to using DSL when deal with complex query.
12 requests per second: A realistic look at Python web frameworks
211–220 of 239 posts
Re: 12 requests per second: A realistic look at Python web frameworks
#212Don't forget that you're paying a huge price using the sqlalchemy orm - https://docs.sqlalchemy.org/en/13/faq/performance.html If I know an endpoint is going to be hit hard, I forgo trying to use the ORM (except to maybe get the table name from the model obj so some soul can trace it's usage here in the future) and directly do an engine.execute( ). Makes a huge difference. Next optimization I do is create stored proc…
Re: 12 requests per second: A realistic look at Python web frameworks
#213Earlier quoted context omitted.
There is lots of truth to this. Some ORMs like Django perform joins in very unsuspecting ways. A simple example is, say, foreign keys. Trying to access the foreign key of an object by doing `book.user.id` does an additional query for the user table to get the ID. It's less known that the id is immediately available by just doing `book.user_id` instead. I've spent time optimising things like text searches down from 20…
I encountered this a few times and started adding tests that assert each handler only executes the expected number of queries (and no more). If the application code is modified such that this N+1 query pattern occurs the test will immediately fail and you go optimise the query, problem solved. https://docs.djangoproject.com/en/dev/topics/testing/tools/#...
Re: 12 requests per second: A realistic look at Python web frameworks
#214Earlier quoted context omitted.
The programmer also already had the value in book.user_id but still chose to ask the ORM to fetch all of .user so they could get .id from there instead. And they might then afterward call .name on it as well, and there would be no further queries, because the ORM has already been asked to fetch all fields of .user - so it might in fact have been sensible to fetch all of .user if so. The query builder cannot know whet…
Perhaps i'm a bit odd, but when I'm going to lean on an ORM to do things I expect it to actually do them. I expect that foo.user_id does not exist, because that representation has been transformed into an object. foo.user.id should be the only viable reference to the id. foo.user.id should return the value it already knows, any other property access i would expect will do the equiv of `select * from ...` if the objec…
To me, it seems like the (hypothetical?) implementation you're talking about is much more leaky and footgun-y than the more straightforward ("thinner", in your words) version. In order for foo.user.id to not execute a new query, foo.user would have to return some sort of proxy object that only fetched the user row when you tried to access a field that hasn't been loaded. That's way more magic than the more obvious solution—which is to load the row when you access the related object—and could easily cause more problems than it solves in the long run when you need to debug very specific queries.
Furthermore, how is going out of your way to hide a field that exists in the database (user_id) not the definition of a leaky abstraction? What purpose does it serve to direct you through an unnecessary layer if all you need is the ID?
Re: 12 requests per second: A realistic look at Python web frameworks
#215Earlier quoted context omitted.
`QuerySet.select_related()` and `QuerySet.prefetch_related()` are the bread and butter of Django query optimisation. I think most of the time that I've noticed a performance issue in our code, it's been easily fixed with one of those.
Django's ORM gets a lot of flak, but I don't remember the last time I had complex queries that I could not do with it. You still need to understand a minimum of SQL and databases, and usually those that complain about the ORM are the ones that expect it to be a "sufficiently advanced compiler", but it has matured so much that nowadays the developers consider a *bug* every time the answer to How do I do this query X?…
Even though Django has come a long way, I greatly prefer ORMs like SQLAlchemy and Ecto that map more closely to the SQL query I'm trying to write.
Re: 12 requests per second: A realistic look at Python web frameworks
#216Re: 12 requests per second: A realistic look at Python web frameworks
#217Earlier quoted context omitted.
I just wanted to know how it would compare with Sanic, because I never used it.
The biggest difference is that Falcon is synchronous while Sanic is asynchronous. With Sanic, you would explicitly specify async/await for asynchronous operations and use asynchronous libraries for I/O. Switching to Sanic could also affect how you deploy to production. Both are plenty fast. FastAPI [1] is also worth considering if you’re looking into asynchronous API frameworks. It comes with nice features for specif…
Re: 12 requests per second: A realistic look at Python web frameworks
#218Earlier quoted context omitted.
I agree with your general take on developer productivity, but I don't feel that modern JS is significantly messier than Python, at least not to a level where it significantly impacts productivity (I'd rather avoid a debate on the abyssal depths of the language, eg, type coercion) I feel about the same amount of grievances with both. For instance I dislike Python's async and functional semantics ( list(map(lambda n...…
> list(map(lambda n... List comprehensions are much better for this. Functional doesn't mean you have to use a function call. If you can use the paradigm with literal syntax, just do so.
Re: 12 requests per second: A realistic look at Python web frameworks
#219Earlier quoted context omitted.
My guess is accessing a related field within a loop causing a database request per iteration, e.g. ``` [book.author.name for book in Book.objects.all()] ```
Maybe I spent too much time with Django already, but if I see anyone doing anything but Book.objects.values_list('author__name', flat=True) for this type of expression, I would mark it as a 3x WTF? in the code review.
Re: 12 requests per second: A realistic look at Python web frameworks
#220Related to ORMs/queries/performance, I have found the following combination really good: * aiosql[0] to write raw SQL queries and having them available as python functions (discussed in [1]) * asyncpg[2] if you are using Postgres * Map asyncpg/aiosql results to Pydantic[3] models * FastAPI[4] Pydantic models become the "source of truth" inside the app, they are designed as a copy of the DB schema, then functions rece…
Do you need to define your models more than once with these? I'm looking for a single source solution and haven't quite found it yet.
Each asyncpg result has a dictionary-like interface, so I can convert it to a Pydantic model easily.