Django has allowed me to enjoy some side entrepreneurship. I have released three products as a solo part time dev that I would never have been able to do in a reasonable time using Java/Spring (my strongest stack). My first project went nowhere, but the second generated 1k+ a month and sold for 50k, and the third one is following a similar trajectory.
My advice - keep it simple
- function based views
- centralize access to the ORM (avoid fat models, use a service layer)
- responsive bootstrap template with crispy forms
- minimal js
- 12 factor type setup - django-environ or similar
- be aware of n+1 select problem (I use django debug toolbar but looking into django-zen-queries)
- plop it on heroku, AWS lightsail, Digital Ocean or if containerizing use AWS Fargate
- avoid celery unless you are at a scale you need it - use django-cron, django-db-queue or similar
- use a managed database and managed email service
I have had zero scalability problems. People say Django is slow but it handled 10k active users on a small EC2 instance without any issues (static stuff served via cloudfront)
If you can avoid Generic CBVs (things like ListView) and inheritance, then the only difference between FBVs and CBVs is that CBVs make it easier to see what's a GET / PUT / POST / DELETE by adding some syntax highlighting that makes it easier to visually differentiate which code goes to which method. You don't need to know anything about classes in Python in order to use them.
It's not at all difficult to switch from FBVs to CBVs later, and most people (myself included) use FBVs when getting started. But I'd also say that if you're willing to push through the initial discomfort and spend the extra half hour or whatever on YouTube in order to understand them, then you do get a little bit of a nicer overall development experience.
Alex has written an excellent blog about Django best practices for startups. It contains wonderful insights about how you should structure code and why you should structure it that way. It has helped us a ton!
https://news.ycombinator.com/item?id=27605052
I usually have a FBV handler (foo_handler) that just passes along all the required parameters to foo_get, foo_post, etc, and then takes the return value of those and wraps it in a HttpResponse/JsonResponse.
That way my actual view logic doesn't have to deal with anything http, and it makes testing them way simpler.
Class views are fine. Even just a TemplateView saves some effort, but it comes with learning some Django magic about the life cycle of the view to load data in and out and what methods to implement. I still recommend it. You don’t have to fully embrace it and it lets you avoid repeating a lot of code.
My recommendation for learning class-based views is to use a good editor or IDE that allows you to drill down into the library code easily. In PyCharm/IntelliJ I can just command-click on the TemplateView symbol and see its implementation, and do so recursively until I've unraveled the entire thing.
Agreed. Django's documentation is pretty light on the order in which certain functions will be executed and diving into the code is the only way I've been able to understand what happens when handling forms and views.
Every time I need to write up a research paper in TeX and insert a graph, I make the same mistake of searching "latex images" and getting some pretty exotic results
I recall a colleague googling Prince Albert in front of his boss whilst discussing a potential project in Monaco. That was before either before safe search was a thing or he had it switched off.
Another classic was (I haven't tested recently) that while Firefox would search if you entered a word that was not an address into the adress bar, while certain older browsers, at least Safari just added .com
I just got results for repairing cellos and violas. "G string" got the expected spicy content in addition to violin repair ads. Surprisingly, no string bass repairs (in spite of the bass also having a g-string)
When I first started using the python tool/ library named fabric, I knew that putting just "fabric" into google would not get me what I wanted, so I instead searched for "python fabric". The top results were trying to sell me handbags and boots.
(These days, the library does rank first for "python fabric").
It's great for getting things up and running. And can last a long time. But now that we're 40 devs or so working in the same 400k LOC codebase, I'd prefer Java/Spring (or really, Kotlin). So hard to maintain django in the long run, need to be really strict, or one ends up with each app spaghettied with other apps. Doing queries where you filter deep on other apps' models, and since it's only done as kwargs with no typing nothing stops that from exploding runtime when someone changes a model somewhere. Too easy to send fat objects around everywhere, accidentally doing heavy db stuff when using a property. Also makes it harder to test, because everything leaks.
Strongly agreed on these pain points. One tool I have been using recently to help is django-seal, which locks the QuerySet so you can’t run extra queries. This should really be part of the ORM, fail-unsafe is a bad option for performance critical code.
Combined with the Repository pattern from DDD, you can have all your model fetches go through a separate class that does the necessary select-/prefetch-related calls, then seals. (Or just override your model's Manager to do this).
And we have a coding standard guideline to discourage using queryset operators in business logic, as you note it breaks encapsulation and makes your code really hard to refactor later. This is hard to enforce though…
I think Django’s ORM is if anything too convenient - it’s great for the first 100kloc but then as you say, you need to overlay some discipline to prevent things from blowing up, and the framework is all about removing friction which makes this hard.
I can see that happening on a large codebase with a lot of devs.
Being a Java guy, I organized all the business logic and db access into a service layer, added type info and wrote a decent amount of unit tests.
I can't deny however that I am 2x faster cranking out crud screens in Django than in Java and the combination of ORM, migrations, templates, and access to the 3rd party app ecosystem is a real productivity booster.
Yeah, for a simple app, it's so quick to get some auth, rest endpoints, a fine admin panel etc.
Problem is when you start abusing stuff. Just hook into some signal to do something, nbd, but suddenly it's an interconnected mess. Just add some custom stuff to the admin page, and suddenly what should have been a custom made page is now a weird mess it's hard to extend. Need to do stuff async in the background, and what could have been adding a task on some queue and having a thread poll it is instead this behemoth of complexity.
A large django app can also be good, I think, but then one at some part have to realize when to stop tweaking built in django features and write custom stuff, to avoid adding hacks on top of hacks. And also early stop doing the easy communication between apps, instead take the annoying detour around services and hard boundaries.
Yes, but nothing is stopping you from typing and organizing everything, right? I agree that it's not the default, which isn't ideal, but you can do it fairly easily (and keep it enforced).
Problem is that the typing with mypy is in general not a very strong guarantee in Python, and combined with Django most of it's almost useless. Sooo many kwargs doing magic stuff, or types being lost after being through some django functions.
And hard to do the organization propably in django, doing it breaks much of the benefits of the orm for instance. People will do what's convenient, and in Django that's writing unmaintainable code.
This seems like an architecture issue, rather than a framework issue. At some point you need to split these 400K and 40 devs out into different applications. Different application should not be able to access each other's database, but rather communicate through an API.
I'm not saying that you need micro services, but you should find a way to split this up into a few applications with well defined interfaces between the applications.
I agree. What we've been doing is - based on advice from somewhere I can't remember - is to always make things easy to remove.
So whenever a new app/module/service is added - the "author" has to think about making said thing easy to remove.
The result is that new apps/modules/services must have very few connections with the other parts of the code. It does result in a fair amount of "get_<app>_settings" on the base classes, but the result is a lot less spaghetti.
The thing is, we rarely remove stuff later on - but when we do, it's often only a few methods that needs to be altered in order to disconnect a specific part of the system.
> one ends up with each app spaghettied with other apps
This is what I always found in Django projects with the notable exception of the one that had only one giant app.
I started to believe that one project doesn't get along with multiple apps (kind of microservices) in the mind of the average developer. Only one app Rails style is probably easier to grasp and manage.
I think CBVs get a lot of unnecessary hate. DetailView, ListView, DeleteView, etc save so much time and repetition for basic CRUD pages. Once things get complicated and you start having to override too many methods, it's often best to write the post(), get(), put(), etc methods explicitly. At that point you are still writing CBVs but it's much more organized.
PaaS and managed services let you trade money for time and effort - patch management, backups, security, config optimization, simplified scale-out/availability/etc. all take time, effort and skill to get right and keep running well over time. With RDS/Aurora/AzureSql/CosmosDb (and the like) you get to focus on your app/service/product instead.
It all ends up being a question of how much your time (and being able to focus) are worth to you. If this is an actual business and not a hobby, and assuming market rates for your time - I believe the managed service offering ends up the cheaper option overall.
I've used ElephantSQL before. As long as you write efficient queries (especially avoiding N+1) and choose the right data centre it's usually reasonable for a small project.
Ideally try to avoid using Celery at all. It's the most consistently buggy, poorly documented, worst quality major Python project I have used so far.
Here is a fresh example: they deprecated CELERY_ prefixes in Django settings for some reason, which makes zero sense [1]. But because it's Celery, they only warn on a few properties but not others, and the tool they ship inexplicably renamed completely unrelated settings for me [2]. And yes, apparently the new way doesn't work either [3].
It's very characteristic of what I've seen from Celery over the last few years of working with it. If you are doing anything greenfield, it's best to avoid it altogether.
The reservation app was super simple so no real guidance there
I'd recommend really getting familiar with the Django ORM so that you understand how it generates the schema and how to tweak it to get what you want.
As far as inventory management - I looked at several opensource projects and tried to understand their schema and use cases. Not just Django ones but other languages like PHP - PartKeepr for example.
Inventory management is surprisingly complicated to do in a generic way.
The most difficult part of inventory management is convincing employees that telling the system that 40000 units moved when only 38732 units moved is a problem
Thanks for the pointers! Yeah generalizing inventory management is very confusing. I tried looking into Oodo but can't get my head around the data modeling. It feels like trying to implement double-entry accounting, but the money can rot/be broken/gone for other reasons.
Asked one of the local investors and got steered to another company that was in the same business altho much bigger. Really just plain luck and I guess having a decent network.
May I ask how you discovered those two profitable niches? Did you already have contacts working there that just told you about it or did you go about hunting it down somehow?
+1 for trying to avoid celery. Too much complexity for simple asynchronous tasks. You should use it however if you are instagram (I think they use a customized Django and Celery).
I’m sorry but I don’t buy that. Spring Boot is very highly productive — I really don’t believe any other stack would fare better by a significant margin.
Basically I didn't want to support it. It was a lot of schools and small businesses and so plenty of "I can't login" and when you work with them it's their wifi or something. The company I sold it too had customer support :)
Django is the nicest framework I've come across! For some context, my favorite overall programming language is Rust. Despite Rust having several web frameworks, I use Python the server due to Django being so nice.
In Python, there are micro frameworks like Flask, and whatever new ones claim to be "Blazingly fast!", async etc. Once you get over the learning curve, Django seems the nicest to use, due to including features like an auto-updating ORM, email, authentication, admin page etc. None of the Rust frameworks, in contrast, come close to feature parity.
Django's feature base is vast, and I admit I don't understand much of it. You can choose the feature sets you need. The template language is inflexible, but you can smooth this over using JS instead A/R.
We went all in on FastAPI with my team, but we're hit the issue that the projects of Tiangolo (FastAPI, SQLModel, Typer) seem to be turning pretty much unmaintained : https://github.com/tiangolo/fastapi/discussions/3970
We've already been hit by multiple bugs with fixing PR opened, but left to rot, and missing documentation : the 'tutorial' documentation is great, but if you want a reference you have to go read the code.
I guess that's the issue when you pick projects maintained by one person, so for my next projects I might get back to something built on stronger grounds, like Django.
How is Django this day with modern Python constructs like typing in general and Pydantic models integration ?
Hey! FastAPI author here. I do have a bunch of issues and PRs to review across the projects. But as I personally review and in most cases fine-tune and update each one of the PRs (if you check the history, almost no PR is directly merged, most of them require updates) it's taking me a bit to handle them all, but I'm on it. I even changed my working structure to optimize for more open source. Sadly, new issues and new discussions like that one linked asking why the other issues are not solved, don't really help, as they just add another issue for me to read and take care of. I'm also prioritizing the work that can have the most impact. For example recently I was helping a bit with AnyIO and Trio, as they are used underneath by FastAPI. And now I'm working on something for Pydantic, that would be used by SQLModel and FastAPI.
One of the things that consumes time the most is handling issues by others. If you go and help them and they close those issues, that's a lot of minutes (in some cases hours) that you save me, and that I can then dedicate to review the other issues and PRs.
Totally understand your point, makes sense, and indeed if the community were to be more supportive with issues/PR it would be very helpful for the project.
Seeing this kind of topic fairly frequently, it would be great to understand the short/mid/long term plan for the project in terms of governance.
I guess we can all agree that having a single maintainer on a +40k stars project with huge adoption (amazing stuff btw) is unsustainable and extremely difficult, and can drive people away from adopting it in big corporations (I've seen a few comments on Github that mention this), and can make early adopters eerie.
I honestly have no idea on how to do that, but some other projects have been able to (fairly) successfully descentralize the development to be able to keep pace with the development.
It's obviously fair and understandable if the long term will still depend entirely on you, being the creator, you obviously have 100% right to do things the way you want, and nobody can argue against that.
I guess it could be helpful to give the community some guidance on what's the idea for the future of project, and whether it will have a more distributed governance or if it will continue being centralized.
Any of those paths is fine of course, I guess it would just help people deciding the framework choice for the future.
Yes, and one of the ways "documented here" is "Create a Pull Request"!
How is creating PRs going to get us "faster progress" when the very complaint from OP is that PRs are left to rot? And some PRs as simple as 1-line critical bugfixes that have been rotting for over 3 months? See, e.g., https://github.com/tiangolo/fastapi/issues/3665#issuecomment...
Shouldn't open PRs be prioritized over open issues anyway? Folks have put in the effort to not just complain about, but to FIX the bugs, and they are ignored.
I believe there's a third-party package "django-stubs" that provides type hints for Django code. For your own code, it's just Python in the end so you can type-hint it as you normally would.
> Pydantic models integration
There isn't as far as I know. Django has its own model layer however unlike Pydantic, Django's is purely for the DB level, it's not there to (de?)serialize JSON.
In the Django world you'd typically use Django Rest Framework serializers for that. Now you can use Pydantic and it probably won't be too much work to make them work with DRF, but whether it's worth it is a different matter - DRF gives you a lot of stuff for free such as auto-generating serializers based on DB models (including validation and everything) which you wouldn't get with Pydantic.
Pydantic allows you to write super clean (in comparison to DRF) serializers using just type hints, as opposed to the declarative approach of DRF serializers. It's really nice and refreshing.
I sometimes get these almost angry emails about Gensim, my open source library. How come I'm not responding faster, merging PRs, fixing issues? For this critical popular project??!
Meanwhile, sponsorships are at 13% of the (not terribly ambitious) goal of $6k/month [0].
> How come I'm not responding faster, merging PRs, fixing issues? For this critical popular project??!
> Meanwhile, sponsorships are at 13% of the (not terribly ambitious) goal of $6k/month
The answer would then be: "because I have to keep another job to pay the bills" then? Hope you can get to your goal ASAP. Going by stupidly quick math, you have 12k stars for gesim alone, and that boils down that if every star on github payed 50 cents you'd already be there. I know that starring doesn't mean anything other than "hey, this looks interesting!", but then again 50 cents is probably cheap enough for a majority of those "stars" that they wouldn't even notice.
Do you ever respond to these emails telling them that they can hire you to attend to their issue? If my business depended heavily on an open-source component that needed a bug fix, more often than not I would pay the person to spend their time to resolve it.
Often it is easier to get a business to pay a consulting fee for a specific thing rather than make a donation that goes into a general funding pool for a project.
Yes, I do. But it turns out that their issue is pretty much always only tangentially related to Gensim (or any other open source library / technology). They're solving a business problem. That's what brings them money, with which they pay me, and I acknowledge that.
This leaves me with two choices:
1. Help narrowly, with the Gensim part only. Pros: easy for me. Cons: doesn't truly help the customer; feels dirty.
2. Help solve their actual business problem. Even if it means not using ML at all (a very common outcome), or suggesting a different architecture.
In my experience, most companies are so lost as to what to expect from ML, or how to get there, that I didn't feel comfortable with the "narrow" help. Lipstick on a pig.
So I went the full consulting route for a few years. But that felt too draining so I dropped it and created a product instead.
I do with PR, and by strongly recommending the sponsored classes (from testdriven.io in the ads on the doc : https://fastapi.tiangolo.com/) for my company, but you're right that I could push harder for official corporate sponsorship.
I share your thoughts on Typer[0]. It's a super cool project, but lacking proper hooking points to interact with the `click`[1] lib underneath [2], I am not sure if it would be the best decision to use Typer when we're rewriting our CLI.
Genuinely curious how you happened to choose FastAPI over something more in the middle, like Flask?
I tried a few tools in FastAPI, and it's fantastic for those, but I think I'd still stick with Flask for something more well rounded (not just as an API server).
I would say Flask is genuinely really difficult to use. It's very easy to start with but the moment your project becomes non-trivial, it's a mess of wires and plugs that resembles something like Django anyways but all maintained by you or your team. FastAPI seemed like something that's in a solid middle ground between something beefy like Django but something light like Flask and it's much easier to start with since it gives you REST and form/JSON validation out of the box
That said, FastAPI seems to have a lot of issues and the maintainer doesn't seem to be responding to the numerous PRs that are piling up. I'd much rather just stick with Django at this point
It's like a FastAPI inspired Django Rest Framework. In comparison to DRF it feels very lightweight and modern, but it's still Django underneath so you get to keep the ORM, admin, etc.
Speaking of the ORM, once Django's is async Ninja will really be amazing.
> I would say Flask is genuinely really difficult to use. It's very easy to start with but the moment your project becomes non-trivial
Do you have specific examples?
I've built a number of decently large Flask apps over the years (dozens of blueprints, 100+ models, etc.) and it was never an issue. I've used similar code organizational patterns in both small and large apps and it worked great. It was a combination of solo projects and working with small teams.
I share OP's general statement. I've been using Flask and Django on and off for near a decade.
When deciding between Flask and Django, I look at the following requirements:
User accounts
Object Relational Manager
Database Migrations
User registration/social authentication
Admin Site
As a general rule of thumb if my project is going to need 3 or more of these things I just go with Django.
You CAN do all of that with Flask, but you end up wiring together a bunch of third party dependencies just to end up with what Django would have provided you out of the box.
As I said, any time a Flask app becomes non-trivial, it's a pain. Engineering scaled up from 10 to 30 and immediately it was apparent what a massive pain it all was. We had to implement auth, access control, caching, API documentation, admin panel etc etc all on our own. Flask plugins are very fickle with a lot of them not being maintained anymore that don't fit our use cases. If you need all these things in a customer facing app, you might as well just use Django. Performance suffers a little but the ease of developer onboarding and batteries included in the framework, coupled with a lot of well maintained popular plugins honestly make Django the de facto choice for web app dev that needs to scale. Flask is good for small services being maintained by small teams
> As I said, any time a Flask app becomes non-trivial, it's a pain
Results may vary. Small teams doesn't mean small apps. You can have 150 models, dozens of screens, caching, background jobs, rate limiting, multiple payment providers, multi-tenancy, public APIs and other features you would expect in a decently sized SAAS app and it's fine. The popular Flask extensions for certain behavior are pretty well maintained. There's also a number of Python libraries that can get used in a Flask app that aren't specific to Flask but are very well maintained like SQLAlchemy and Celery.
I wouldn't say Flask is better than Django or Django is better than Flask but I think you can be wildly successful building an app with either one. It comes down to personal preference. Your post made it seem like Flask is some graveyard of dead libraries and you can't build anything successful with it but that's not the case.
At least for my team we went with FastAPI because we have other tools for everything else, and FastAPI is incredibly simple for new members to pick up. We tend to say that we made a “FastAPI application” but in reality FastAPI is only powering the REST interface for interacting with the platform.
We use SQLAlchemy for database interaction, a lot of direct usage of Pydantic, Celery with RabbitMQ/Redis for task processing. FastAPI provides a really lightweight interface to gluing pieces like this together.
> We use SQLAlchemy for database interaction, a lot of direct usage of Pydantic, Celery with RabbitMQ/Redis for task processing. FastAPI provides a really lightweight interface to gluing pieces like this together.
Just wondering, why have you not gone with Django directly if you’ve essentially ended up reimplementing your own version of it?
that's how i feel about all these python "micro frameworks".
you start simple, but then you realize you need some kind of db access, some kind of caching, some kind of authentication, maybe some admin interfaces.... aaand you re-implemented django yet again.
Exactly the reason why I don’t care about either FastAPI or Flask. In my experience you’ll always need more than what it comes with out of the box and you’ll end up building your own (crappy) Django.
There wasn’t really an aversion to or direct decision to not use Django, it just kind of naturally evolved. We were experimenting with FastAPI in some mock APIs used for test suites and some different projects and decided to incorporate it into a larger project.
The pulling in of other tools happened naturally over time. Perhaps we reinvented the wheel and Django may have saved some time or effort, but ultimately in the end we gained a very deep knowledge of the system we have and the things that make it work, and how those individual pieces might be usable in other projects/scenarios.
A large driver was honestly just that we had no organizational experience in Django, and so there was no one that even would have looked at it and said “Hey we’re just recreating what Django already does”.
I used to really like Flask but after trying FastAPI I doubt I'd ever go back. I even rewrote the entire backend for our product in FastAPI and unlike some rewrites I've done in the past am 100% happy with this one. Major benefits for us:
- Faster in general than Flask (thanks to starlette / uvicorn)
- asyncio libs in Python are generally beating out others in terms of perf (e.g. asyncpg vs psycopg2). This isn't to start a whole "asyncio is faster" flamewar, not saying that's necessarily true. Just that it seems the people who are building in asyncio are also people who care about perf, which I do as well. The (non-asyncio) Python community has neglected perf for a bit too long imo.
- asynchronous style works much better with things like WebSockets, which we use
- Auto-generating an OpenAPI schema[1] is nice to have.
- Using type hints for things simultaneously encourages mildly "safer" code while also giving better auto-complete suggestions
Experience told me to be very conservative when picking frameworks. For small projects and personal, sure, just pick whatever you want and have fun! But for serious projects, you should stick with old and stable software.
I made the mistake of picking Flask for a really big project when these microframeworks were a fad and everyone was dismissing the big ones like Django and RoR. That was a very costly mistake. I learned the hard way provides way so many things out-of-box that I always took for granted but I had to manually implement in Flask.
Take tests for example, Django automatically makes sure that any DB operation is cleaned by the end of the test. That's sometimes that I never gave a thought until our Flask tests started leaking everywhere. The way that Django handles that problem is very clever and we had to manually do it Flask.
I'm thinking about using Starlette directly. It doesn't look that much more verbose than FastAPI and I already disagreed and bypassed a few FastAPI features. I wonder how much work would it take to hook the Pydantic models with their SchemaGenerator.
It pulls together Django, Docker Compose, Postgres, Redis, Celery, Webpack and TailwindCSS. It's all set up for both development and production. It's also been updated to use Django 4's new built in Redis cache back-end instead of the django-redis package.
Thank you very much! I've been running my own clobbed together version of a Django + Webpack configuration, and I'm very interested in learning from your code how you manage to run only one container with both Django and Webpack on dev mode.
I have things split out where the Webpack dev server runs in its own container only in development. It uses the Docker Compose override pattern to skip it in production along with Docker's multi-stage builds to ensure production assets get built and digested with collectstatic when DEBUG is set to false.
I gave a talk this year at DockerCon on a whole bunch of patterns I use with Docker Compose and my Dockerfile to get dev / prod parity without duplication and overall best practices.
Django made me fall in love with programming. I built 2 decent-sized side projects with Django. They didn’t gain traction, but I loved using the framework.
Then I went through a couple of (depressing) years of using Java/Spring professionally, and recently I made the choice to move to Elixir/Phoenix. It’s going great, and I don’t think I would go back to heavy OOP if I didn’t have to.
Some things I really miss from Django:
- Models with more centralized validation logic. In Phoenix (and somewhat similarly in Rails), you need to edit your migration, schema, and changeset. In Django, you edit your model and the migration can usually be made automatically with no need for a 3rd-party package.
- Django REST Framework. You can get pretty far with an app just by leveraging ModelSerializers and ModelViewsets. It’s ridiculously elegant, simple, and powerful.
- The Admin layer is fantastic and unparalleled.
Some things I wish Django would add/enhance:
- Making it more clear when you’re going to hit the database (explicit is better than implicit).
- True async up and down the stack. Not a small task within the Python ecosystem, but I think for folks who are not already invested, looking at Elixir/OTP/Phoenix is too tempting. If you don’t have kids to transport with you, a Lambo looks much more fun than an S-Class Sedan.
> - True async up and down the stack. Not a small task within the Python ecosystem, but I think for folks who are not already invested, looking at Elixir/OTP/Phoenix is too tempting. If you don’t have kids to transport with you, a Lambo looks much more fun than an S-Class Sedan.
This has been in the works for a few years. Everything in Django works with async except the ORM at this point. Which in practice, means it's not really async. But hopefully they are getting close.
I recently upgraded [0] one of my project [1] to Django v4, from Django v1. The changes from Django 3 to 4 were fairly simple, just one line change in my case. The change log is extremely detailed, it was easy to upgrade. Also, I found a tool called django-upgrade [2] which makes some changes automatically, rest I made manually.
If the 1.11 project is in python 3 then it would be a piece of cake. If it's in python 2 then it will need a little more effort to convert to python 3 but but not much really. Django have hasn't changed that much since 1.11!
I don't think I overstate the relevance of what Django did by saying that Django almost single handledy advanced the state-of-the-art of web deployment in Python forward a decade.
Zope hails from the era when Python was trying to appeal to programmers by showing that it, too, could be a Serious Enterprise Language. Everything had layer after layer of abstraction and the code looked more like Java than Python. The Zope ORM was kind of nice in its own weird way, though.
Zope had some things like the Language Is The Database trope that sort of remind of the things described in those Bank Python blog posts that have been floating around recently.
- The new AddConstraintNotValid operation allows creating check constraints on PostgreSQL without verifying that all existing rows satisfy the new constraint, meaning that one can now create constraints on big postgres tables without a waiting period.
- Async methods are coming, to cache for now but hopefully soon to the ORM. They are prefixed with a `a` in the name
- DeleteView now uses FormMixin, allowing you to provide a Form subclass, with a checkbox for example, to confirm deletion. In addition, this allows DeleteView to function with django.contrib.messages.views.SuccessMessageMixin. This is a big thing if you use DeleteViews, as you can now easily show a message post-deletion to the user.
Yes - it's kinda ugly at the moment, having all the `a` prefixed functions - but I think it's intended as a kind of long-term-intermediate step - since the whole of django isn't async at the moment, and needs to have separate versions for everything.
I (wildly) speculate that once the ORM is async too, and all the rest of the bits fall into place (maybe django 5?), maybe django 6 will drop the `a` prefixes and integrate it all back together somehow?
I believe they chose the 'a...' pattern as it matches other apis in the standard library - it also makes it explicit but concise.
I can't see it being dropped any time the future, Django will always have a sync api, that won't be dropped. And there is no way in (current) python to combine a sync and async api into a single method.
I enjoy coding with Django. It's the framework that helped me make a smooth move from PHP/Symfony to Python-powered web apps. Not that I dislike PHP... just wanted to focus more on Python.
Just for reference: The latest release for Django Rest Framework is not compatible with Django 4.0 yet. At least my first attempts failed due to a missing `pytz` dependency. This is fixed in the `master` of DRF on Github. Installing `pytz` explicitly again to your project fixes DRF for now.
> The Python standard library’s zoneinfo is now the default timezone implementation in Django.
> This is the next step in the migration from using pytz to using zoneinfo. Django 3.2 allowed the use of non-pytz time zones. Django 4.0 makes zoneinfo the default implementation. Support for pytz is now deprecated and will be removed in Django 5.0.
> …
> To give time for such an audit, the transitional USE_DEPRECATED_PYTZ setting allows continued use of pytz during the 4.x release cycle. This setting will be removed in Django 5.0.
Love Django, yet the transition from local development to deployment, in my opinion, continues to be just plain ugly and painful.
I've written about this before. I think the stock Django dev configuration needs to change to something that is equivalent to a reasonable deployment on a common VPS, whatever that means. The development server is a nice trick, but the things you have to do to move to a real server are painful. Same with SQLite. Why not come up running PostgreSQL by default?
I am sure there are varied opinions on this. All I have to say is that, over the years, I developed a document to help with deployment on standard hosting platforms, like Linode. It is staggering when compared to the experience of deploying something like a Wordpress/PHP site on the same or similar platform.
This, from my perspective, is the weakness for Django, everything else I love and enjoy.
There is a huge doc[0] for a standard "deploy decent prod setup to VPS." What happens after you make some changes, something goes wrong with the server, and you have to go through all these steps again, only that it gets you to Day 1, not Day 90? It leaves a lot to be desired for quick, reproducible deploys.
Ime, adding a docker-compose and a couple Dokku commands on the server is the best experience I've seen so far.
Not sure that your link is an answer to my comment.
Whatever the case may be, I can take a single VPS and multi-host a bunch of domains, each with it's own Wordpress installation, DB, etc. Virtual hosts makes this possible. It's stupid simple. Getting a single Django application running under a single domain on a VPS is a nightmare. Particularly when you consider everything you have to do to migrate from the development environment to production.
This is why we do not do any Django development using the standard installation and go right wo running it just like production on either virtual machines or a dedicated on-premises Linux server. The development environment is as close to an exact match as the production environment. No SQLite, no runserver, no nonsense with static files, etc. This is what I think the Django project needs to really think about working on. There is no reason to have a crippled development environment at all. The base installation ought to be setup to migrate to a standard Linux host (not something purpose-built like Heroku) without much friction. If you are doing a more complex site with multiple servers, load balancing, etc., you are on your own.
I truly think adoption would be far greater if the deployment experience was sensible. I have talked to people who absolutely gave up on Django because going from development to production was a nightmare without spending money on far more expensive servers and services they should. This is an application (Django) that should easily run on any Linux server. The fact that companies were created to facilitate deployment says it all.
I agree, although as my other comment states, the most frictionless way I’ve found is to make a quick docker-compose, and run maybe 5 Dokku commands on the server. Then from dev environment just ‘git push dokku’. That’s the level of friction we should also expect from Django built in.
I missed that in your prior comment. Yes, I think you are right. Docker --which I kind of hate to admit-- seems to be the only low friction solution at this stage.
This is why I insist that the Django dev team ought to put some thought and effort into fixing this issue. If Django installed in a realistic baseline production configuration (not a toy DB, runserver, etc.) and came out of the box with a super simple mechanism to deploy to the average VPS the framework could become 10x more popular than it is today.
Nobody could blame a developer looking that the Digital Ocean deployment tutorial you linked to and thinking "f-ck this!". I can even see said developer using that as an argument against Python/Django if management was involved in making a platform decision.
There are dozens of articles just like that one that are multiple pages long. To me that constitutes absolute proof that deployment in Django is seriously flawed, if not broken. I hate to say that because I love the framework, but c'mon, the fact that such articles exist pretty much says this is ridiculous.
I must learn Django, it's got everything I'll ever need. No, Rails will make me happy and it's better for lonely devs like me. No I must learn a Modern Web Framework (TM) to prove I'm not stuck in the past. No way, I'm not a fashionista. Have you seen that Phoenix demo where the guy builds Twitter in like 4 minutes? That's the future. Hold on a minute, enough of this magic. I need to learn to build everything in Common Lisp and own my code 100%! Wait I don't actually have the time for that. Django is the way, it's so solid, just that it won't bring me joy....
That would for sure push our community to better describe the runtime of the different solutions. We talked only about syntax, features, semantic but very little about the runtime characteristics of the different platforms (Java, .Net, BEAM, different Common Lisps implementation etc). Maybe it's a sign of abundance and that most of these solutions are ok in most of the cases.
Different cars are much more obviously different than different web frameworks. When car shopping, you know whether you're looking for a RAM3500, an Odyssey, or a Mustang. Sure, the F-150 may win out in the end, but the Caravan and Charger were never contenders when looking for a truck.
Choosing between software (especially bikeshedding between languages that both place priority on being pleasant to write) is often like comparing a Tacoma to a Tesla on the quality of seat heaters, upgrade price for the sunroof, and selection of factory paint options. Worse, you can't often have a clear understanding of the difference without investing the time to gain direct experience with each option. Otherwise, you're relying on community reputation and hoping it's both accurate and not horribly out of date.
Imagine each car model having different controls, different engine and components placement, some coming with rectangular wheels. That image is closer to SW frameworks.
It's actually a bit worse (or better) depending on how you see it.
You get to invest (time, energy) in what will land you a job.
99% it's not what you want/like and not what is technically best. IT's just what came to dominate your job market.
At least if you're in a EU backwater country or in EU public sector-related projects. (Which usually means java/c# and to a much lesser extend a python tech stack).
PS: Loved the rant but you forgot to mention Golang :). "You're doing web backend and not using Go?!!"
This really depends whether you see IT as a job or as a tool to achieve your own projects. If the latter, then you don't particularly care how "hype" your tech stack is as long as it successfully solves your own problems.
There's also the part where you dig around to see what people use and recommand, and most of the answers are "use what you already know", which doesn't help when you don't know anything in detail. I feel like at this point the best strategy would be to list all the viables alternatives, pick one, and forbid yourself from using anything else for the next X months, but when thinking about that I have a huge fear of missing out.
You can experience all of this while staying in a single language ecosystem. Just recently, I had to build something in Javascript and got to know Express is no longer recommended. Fastify, Remix are the latest hotness. :)
The difference between Django and Express.js is that one is a fully-fledged web application framework, while the other is a micro framework that just gives you a simple request -> response abstraction over `http.createServer`.
I find Flask is more comparable to Express.js in that regard. Need auth? You need to:
1. Install a lib for persistence
2. Set up cookie sessions, or install another lib for JWT
3. Set up auth middleware
4. Prepare login/register/forgot pass views and flows
which are almost the same steps you need to take in Express.js.
I guess my surprise came from asking around about what framework to use and being told it was Express. I guess it might be 'Sails', but it's been a week or two so that is probably obsolete now.
Nah, Django makes you sad: async support is not full, migrations engine is pretty poor, not all database features are present, any kind of typing is hacky, very slow and so on
There is at least one way that migrating a + b is different than migrate a + migrate b, in ways that you can then only incrementally migrate your project rather than building from scratch.
It’s mainly in post migrate actions, and specifically for me adding a group for permissions.
If you write manual migrations, then it's often worth it to write the reverse function (in your case, deleting that group) so that you avoid this situation.
It’s not the back and forth, it’s that a migration in b that requires the post migrate step from a will fail if both migrations are run in one shot, (say, because you’re bootstrapping a new dev or test db) but it will work when done incrementally (such as during development or deployment).
"Better" for whom? It was a long time ago that I surveyed the ORM landscape but I remember thinking at the time I was much happier using an active record style ORM.
I guess I'm saying I'm always suspicious when I see "better" with no qualification. Everything has tradeoffs.
I have a side project / website that I want to work on. I have over a decade experience with PHP and JS in various forms, but am willing to try something new.
The idea of compiled languages don't appeal to me, so am not really interested in Go, Rust or Dart. It looks like either Django, Elixir, or PHP - but am open to other ideas.
Anyone with enough experience to tell me confidently that it is worth Django or Elixir? Anyone can give a five line overview of the differences vs PHP or Node?
And along the way, am I the only one who is confused by what seems too many environments and package managers? (from venv/virtualenv[1] to pip/easy_install to wheels/eggs)
Django: Great if you are building only a backend or rest API with a separate frontend (an SPA in React, etc). The templates system is terrible, has no asset pipeline built in and frontend on it feels like developing in the 90s. You'll miss php a lot here.
Elixir: Technologically "mind blowing", although all the nice things come from the Erlang VM and not from Elixir itself in my opinion. You will likely not find any job using Elixir, if you do it is going to be full of people just learning the language so the codebase will be very likely a disaster. You will miss a lot of libraries and you will have to write code for things you'd take for given in the PHP ecosystem. Be ready to fight your editor plugins (depending on which one you use, and the degree of IDEisms you're used to).
I understand you want to learn something new, so the most "radical" one here that will teach you more new things is clearly Elixir.
But other than learning purposes, If I were to build something for real, I'd stick with PHP and Laravel, it's an incredible productive, well documented and well thought out platform to develop in.
No difference, it is just a matter of preference and selecting the best tool for the job. Often times the best tool for the job is the one that you know how to use really well.
If you’re good with Laravel for example, no need to switch to Django or Elixir and vice versa. Stick with what you know and you’ll be a happier developer.
IMHO, Elixir & Phoenix are such a breath of fresh air, plus power, plus adaptability, and I love the functional capabilties. On the other hand, if your want lots of numeric computation tooling, AI/ML, stats, etc. then Python & Django have better/more math-adjancent integrations.
I have a lots of experience with Django, and a few months experience with Laravel.
I think Django is good at pure backend solutions. When you're not doing the frontend in Django itself but instead doing a separate SPA. Also the admin is a great tool as an internal CMS. Django rest framework is awesome too.
Laravel is a real "full stack" framework, it has a very powerful templating system (one of the best I've seen!) where you can do components instead of just includes. It has a powerful assets bundling pipeline, great integration with alpine, Vue, inertia and the super awesome livewire.
Laravel documentation is TOP.
As I'm not a fan of the SPA approach and still like frontend and building applications, I much prefer Laravel nowadays.
As someone who's used both - Rails packaging and environment maintenance has been so much less of a headache. Gems and Gemfiles and Gemfile.lock vs having to choose a Python package maintainer (Pipenv vs Poetry?) and deal with that has been worth it to me.
In terms of how they "feel" to develop with? About the same. Similar ideals - skinny models, service layers, REST-first but you can make it RPC-style if you want...
I think that was a fair criticism a year ago, and it's hard to believe it took this long to get a sane dependency management system, but now the answer is use poetry every time.
Basically if you have two dependencies that depend on the same package, but depend on different versions or non-overlapping ranges of versions, Poetry's only solution is "tell the maintainers of your dependencies to update their pyproject.toml" - building your package will just fail, with no workaround other than to fork the dependencies and update pyproject.toml yourself. Yes, in principle that sounds like the right way to resolve it, but in practice there are lots of Python packages with overly narrow dependency version ranges (or that are pinned to a single version), the maintainers understandably aren't always that responsive, and forking all your dependencies isn't a great solution.
Yes, and that's probably the right default behavior. But with pip I think you can still work around it, e.g. by installing the sub-dependency manually and then installing with --no-deps.
A couple of comments here have talked about the benefits of “skinny models”, but that runs counter to the advice I’ve seen. How come skinny models? Is the idea to put most of the business logic… where?
I'm unconvinced by the "skinny models" and "service layer" arguments. Django's ORM is an active record style ORM, its at its best if you use it that way. If you want to have a service layer type architecture, use SqlAlchemy with implements the data mapper scheme.
I follow this general rule of thumb, in order:
If it's a complicated business logic touching multiple models or external apis, put in in a `utils` module (keeping this sensibly organised by model/role/activity depending on what you are building, i.e. utils.{model_name}.{methods} or utils.{e.g. order_processing/email/accounts}.{methods}). You can add a shortcut to it as a method on the model if this makes sence. (some may argue this is a service layer)
If I'm only doing something once in one place (in one view), then keep the logic there.
If it's a custom create method, stick it on the models manager class.
If it's something you want to do to a set of objects (from a query set), put it on the manager class or a QuerySet subclass (to make it chain-able).
If you haven't put it elsewhere put it on the model class, as a method or a property if it seems cleaner (don't do get_... and set_... use a property)
The argument is that Active Record is itself a bad pattern that you should not use. And it may be. But it might be a long way down the runway before you run into the bad effects.
Yes, exactly, I agree there are valid arguments against active record, partially at scale. However, I believe, certainly when starting out on a new project/startup, sticking with the standard recommendations and patterns provided in the docs for Django and DRF you make life much easer for on boarding new people to the project later on - they will have seen it all before.
If you get big enough where the standard patterns don't work anymore, YAY! you got big, celebrate! Then start looking at how to refactor for easer maintainability for your specific use case and scale.
The problem I tend to see is that the refactoring never happens. Instead the system becomes the legacy mess nobody wants to work on and eventually it gets replaced. If you get lucky, it'll be replaced by the people who wrote the first version, but it's more likely to be replaced by a team who weren't there when the first one was built, who will build it in this-year's-shiny, without learning the fundamental lessons of why the first version ended up that way so doom the next version to a similar fate.
IMO service objects are an OOP-cargo-cult abomination, especially the kind named after a verb with a "call" method (CreateFooFromBar#call), so the first thing I reach for to offload my business logic involving multiple models or just lots of logic are PORO domain objects with meaningful non-generic method names (a CampaignReservation with "#create!", "#persisted?" or "#available?" methods, a XlsxClientImport with "#perform" or "#check_format" methods).
One day of learning the slightly more interesting Ruby syntax, one day of learning how each thing is called in the other framework, and then a few weeks until your code in Ruby stops looking like Python (i.e. you start using the fancy Ruby stuff).
If you are starting a business, use whatever language you already know. The benefits of one framework vs. the other are a rounding error compared to language fluency. (They are both great, in other words.)
If you are learning both language and framework from scratch, check the local job market. 5 years ago in the Bay Area there were roughly 2x as many Rails job postings as Django, though I doubt it is this lopsided now. Also Python is used more broadly than Ruby, eg for ML and scientific/numeric work.
I'm biased because out of the two I've only used Django, however since learning Django my urge to learn Rails has gone down quite a bit. Its still there, and Rails does look fantastic, but so does the hundred other frameworks and languages on my todo list.
I guess the only takeaway you can get for that is that Django, at the very least, works fine, and probably will solve the problem you come in with unless you want it solved in a very specific way
I recently built a fairly simple backend with a GraphQL API (fairly basic but bespoke e-commerce functionality, a few related models and a few queries/mutations) using Django, and then rewrote it in Rails, as a newcomer to them both (I knew bits of both languages but nothing really of the frameworks).
The reason I switched is more or less entirely that I hit performance issues with the Python Graphene library being really slow at returning larger datasets, and couldn't find a solution in a reasonable time frame.
My impression was that Django has a much more powerful and pleasant ORM – your migrations are derived from your models, so if you want to add or remove a field (for example), you update the model class then the migration command works out what it needs to do to make the database match the models. Rails works pretty much the other way – you create explicit migrations to add/remove columns, and the database is the source of truth - your model classes don't even have explicit accessors in the code for the fields you define in the DB. I found the Django way more logical, and I found things like many-to-many relationships much easier in Django, but the Rails way isn't too bad once you get used to it.
Rails is well known for being heavy on the "magic", which is quite expressive once you get used to it but I find it hard to know where to look if I want to e.g. know what methods a model has, whereas Django felt a bit more explicit in this regard. Personally, I find Python a more pleasant and easier to read language than Ruby, which has so many ways to express the same concept in different ways, but I am actually warming to Ruby.
If you're interested in adding type checking, it seemed that Python has better and more mature options here.
Ultimately though, for what I wanted (which was to build a backend with sensible defaults out of the box and really write as little code as possible), I think Rails is a better fit. The ecosystem of libraries seems more mature and better documented (I hit quite a few issues with Django related libraries which took a lot of Googling and some hacks to solve). The Ruby GraphQL library is much nicer to work with and much more performant.
The amount of code I have to write is generally pleasingly low (though I'll admit it's quite spaghettish already, I have been learning Ruby as I go and I can totally see how a large codebase could become a mess), and it's easy to Google for most stuff, and the defaults seem pretty sane. I will say on a slight tangent that I recently enabled GitHub Copilot, and am finding it surprisingly quite useful in a Ruby project, where I often struggle to know/remember the right syntax!
So... that's my experience. Ultimately it came down to a niche-ish requirement (performant GraphQL API) to force my hand, but I think I'm pleased with the end result. Both are impressive frameworks and I don't think generally you could go too far wrong, but for me Rails just feels that bit easier and more mature for bashing out a solo project.
Django for me has been a solid API gateway. That said I would appreciate an API-centric iteration of it. Something like how Laravel has Lumen. Even if they would make Django ORM available as a separate library it would make me glad. I can’t say I’m overly enamored by the red wave that has passed over python. The only things I really like it for (so far) are when I wish to make concurrent http calls. For whatever reason, I don’t enjoy it as much when I’m trying to serve multiple requests. I prefer keeping requests read only or at most job launching.
This is going to be very painful isn't it. I wonder how long until the bitrot makes it unbuildable and undeployable.
I should clarify, there is no database attached to this and it talks to a secure API that is maintained really well. So this is just views and templates, where the views talk to the API.
I've used Django for a long, long time (since 0.96). Django is great for backend stuff, but nowadays for full stack applications (if you're not doing an SPA) I find Laravel and Rails a lot better. In particular Laravel's templates (blade) and asset handling is awesome.
I've always followed Django as Python is my first language and still my 'natural' way to think about code. The more I look into it, the more I feel like the learning curve is a bit like a camel's back. Easy for some stuff. Hard to figure the middle part (usually because not understanding "how" Django really works). And easy at the ends after knowing how you can "mould" Django in certain ways. It feels like a great multitool for the professionals who need to create project after project.
I believe the middle part of the learning curve can be easier, but with the "social cruft" of all the Django tutorials out there, it's a game of luck to encounter what's good and what are the different, sensible, approaches to handling growing/big Django apps.
What can make this easier is a document/discussion of some sort that provides clear, complete, approaches. Here are some aspect that needs to be considered. I believe with the combinations defined, and describing the tradeoffs will be a very great start for any team/person looking to jump into Django, or improve their next project.
- Postgres seems to be default. SQLite + Litestream is there but still bleeding edge.
- Packaging is the hot topic, for sure. Pipenv, poetry, venv, etc?
- Traditional Serverside Templating + JS enhancement (HTMX/Hotwire/etc), or JS based SPA (React, Vue, Angular, Svelte, etc)
-- This also includes the asset compiling story (Tailwind JIT, Sass compilation, Babel/webpack/rollup/etc)
- Fat models, Fat views, or Service layer? To which extent?
- Single app (Doordash) or Multiple apps (Thread with >500 apps)?
- What's the theoritical limit to using Postgres + Disk/Memory Caching (so no external Redis/ElasticSearch/other services needed)?
- Documented limits about using whitenoise to serve static assets, and when & how to move to S3. Also Cloudflare/CDN fronting
- Cloud storage story (uploading to S3 api from FileField and Rich Text fields, and also async jobs)
-- Proxy story to enable <a download> from cloud? (Maybe a bit far, but I think this is a common request)
- Deploying/Serving optimization (is gunicorn the best option for now? how to scale w/ regard to vCPU counts to avoid the 25% CPU Max utilization?)
- How to debug between inefficient query/app code/templating/serving/caching?
I'm sure there are more points, but it would be great if there's a documented sensible approaches to evaluate. I know there are templates, but great discussions about the tradeoffs of complete "packages" are rare.
Phoenix has a much smaller footprint than Django. I would put Phoenix closer to Flask in terms of how much work the framework does for you. Phoenix has lots of generators which make hooking into the database layer very easy and bootstrapping new endpoints is also fast in Phoenix. Django makes more decisions for you (authentication, permissions, admin, the ORM to name a few).
In the Phoenix ecosystem you bring each of those pieces in as you please (auth generator, Ecto + other libraries etc). So I would say if your really want to move fast, you will still get further, faster with Django - but the trade-off (as other posts here have alluded to) is that once you need to implement custom features or optimisations you will often fight against Django (or need to wade-through half a dozen classes in some inheritance tree to find the right hook).
Another big difference between Phoenix and Django is that building websocket services in Phoenix is an absolute breeze. The Elixir runtime maps perfectly onto any type of messaging service.
I still have a project that runs on Django 1.6. Can't imagine trying to migrate now, when it relies on so many old packages. Would probably be better of starting from scratch.
I would start by replacing the dependencies that are not maintained anymore (i.e. that don't support a relatively recent Python 3.x) with more modern alternatives one by one instead of rewriting it. The migration of the Django application itself should not be that hard.
I had a project running on 1.3 until very recently. The was a lot of issues with the TZ datetime fields, migrations, Python2, etc. It was a lot easier to start a new project from scratch.
Django releases time based and a version 3.3. was part of the plan. 4.0 is released right now because it is December 2021. The versions after that will be 4.1, 4.2, 5.0, 5.1 etc. [0] It's different from how many other projects work but not unique, Ubuntu works in a similar way for example.
And although it might be different form what you're used to, I really like it. If you check their documentation, you can see exactly when they'll make breaking changes and features will be deprecated and removed.
Absolutely, I just question the premise that “most” Django applications are like that.
My best guess, and I have nothing but my own experience to back this up, is that for every public facing Django based website, there are at least three used internally and at least one of those use nothing but the build in admin interface as a UI.
My advice - keep it simple
- function based views
- centralize access to the ORM (avoid fat models, use a service layer)
- responsive bootstrap template with crispy forms
- minimal js
- 12 factor type setup - django-environ or similar
- be aware of n+1 select problem (I use django debug toolbar but looking into django-zen-queries)
- plop it on heroku, AWS lightsail, Digital Ocean or if containerizing use AWS Fargate
- avoid celery unless you are at a scale you need it - use django-cron, django-db-queue or similar
- use a managed database and managed email service
I have had zero scalability problems. People say Django is slow but it handled 10k active users on a small EC2 instance without any issues (static stuff served via cloudfront)