Depends what you want to do, and why you're doing it. Very often when I make something in Flask, I end up with so many dependencies it might as well be Django (but without the cohesion). On the other hand, Django might be overkill in some situations (e.g. a small API without a relational database backend).
I was forced to fall on this trap once: a guy has a small Flask app up and running, and he asked for help to implement: an ORM with migrations, an Admin and form validation (among other things that had nothing to do with Django such file parsing).
I suggested to migrate the app from Flask to Django while it was small and simple, but he refused for Django is "too big and complicated". OK, then. We ended up creating a monstrosity of Flask half-assed packages to include functionality that is already included in Django (think cache, csrf, admin...), plus WTForms, plus SQLAlchemy, plus Alembic, hard to test, hard to keep updated, hard to deploy, and so complex that it turned non-migrable to Django. Development felt like walking through mud.
I love the paraphrased quote. It’s a bit ironic in that Python programmers love the batteries included standard library but quickly jettison that idea when it comes to Django vs Flask.
Flask’s marketing of a simple route decorator makes it seem light weight but once you add everything else it becomes Django.
but he refused for Django is "too big and complicated". OK, then. We ended up creating a monstrosity of Flask half-assed packages to include functionality that is already included in Django (think cache, csrf, admin...), plus WTForms, plus SQLAlchemy, plus Alembic, hard to test, hard to keep updated, hard to deploy...
Heh. I've done that. Impressive how quickly you can go from "I literally need three or four REST end points to let users query this SQLite database" to, well that.
I think one common misconception is that Django can't be used in place of Flask when you want a minimalist set up. And to be fair, I used to think the same until I read Lightweight Django [0]. Their smallest django project code is just a couple of lines:
import sys
from django.conf import settings
settings.configure(
DEBUG=True,
SECRET_KEY='thisisthesecretkey',
ROOT_URLCONF=__name__,
MIDDLEWARE_CLASSES=(
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
),
)
from django.conf.urls import url
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World')
urlpatterns = (
url(r'^$', index),
)
if __name__ == "__main__":
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
Granted, it's not as terse as Flask's hello world example but it's still quite short.
I think the comparison is not so much lines of code but conceptual overhead. With Flask you create an app instance and call @app.route() with some Python functions and run the app. Granted it's probably not going to do much, and by the time you build out a real-world application with a SQL database, authentication and so forth you're going to get diminishing returns, but for a beginner who just wants to know "how do I make a web page using Python?" it's great.
How big is the smallest Django project after you add proper authentication (signup, login, pw reset, oauth2, 2FA)? Last I checked it was rather terrible.
Django has a substantial learning curve and you may need to spend more time to get started. But ultimately to do the same thing in Flask you have to spend comparable time to learn, except that Flask lets you start much quicker into your learning. As your Flask project grows you find out it is not actually that easy nor convenient.
This is of course my experience only. I decided to just stick to Django (and DRF) and I feel no need to use anything else.
For performant services I use Go.
I generally agree philosophically but PHP has a couple of features which make problems more likely: the C style error handling increases the odds of problems being ignored (yes, Python can have except:pass but that’s more obvious & requires intent) and the mushy typing hits even experienced developers who didn’t think about whether they needed === instead of ==. The standard library’s inconsistent parameter ordering for related functions probably deserves a mention here, too.
I started using PHP around 1998 and quit cold-turkey about a decade later for Python after noticing across the board quality improvements on every project. PHP isn’t terrible but using it requires constant diligence and few teams had the extra time and skills to use it safely.
Then lets look at those semantics: what is bad, and is anything ever bad? Because I think mostly all frameworks are pretty easy in the small, it's how they help/hinder with larger projects that demonstrates their worth.
On that basis, sure, it's up to the user to figure out what's appropriate; and a shovel is not an appropriate writing implement - you could say it's even a "bad" one.
1. It’s tricky to use in non-web contexts. If you ever need something to happen as a response to an event which is not a web request, Django makes this difficult and ugly.
2. Django also more or less requires ownership of the database in order to function as intended. If you want to have your Django objects in your own database and handle schema changes centrally (to, for example, alleviate problem 1 above), you must also abstain from some of Django’s power, i.e. migrations, etc.
For (1) The simplest is probably just running a Django management command - maybe from a cron job.
But maybe I'm misunderstanding the kind of task you have in mind?
For (2) - you seem to be saying "if you don't want Django to handle migrations then you lose the benefit of Django handling migrations" - but again I might have missed some subtlety here?
For 1. I have found that a custom admin command can go a long way. If you want to be more "reactive" have a look at this asgi/channels thing... That or custom pythons scripts with the caveat of pointing correctly the DJANGO_SETTINGS ...
As for 2. Well... Basically every ORM no ?
I've had the opposite experience, working smoothly with Django for a decade except for one huge Flask project. Which essentially re-built Django... badly.
I later tried and enjoyed Flask for a really small solo project, so I don't think you're wrong.
Maybe there's a lot of implicit cultural knowledge on what packages work best to compliment Flask and the original developers on the big Flask project I suffered chose poorly?
I also used Flask and I wasn't a Django fan. But this got me on larger projects, when Django and his "Batteries Included" made sense. Now I will soon think about the size of the project in advance, and based on that I will choose Django or Flask.
There really is not something wrong here. Everything has its pros and cons.
Right tool for the right job. You’re not wrong unless you are - if as others said you’re reinventing the wheel on flask, or are doing a very simple API service in Django which doesn’t use most of its facilities (like the admin) and could easily be done in flask.
Honestly, I don't see the point of using Flask anymore. You can set up a Django project and just use a single module with a bunch of view functions, built-in testing and sensible defaults. Done. Not choosing that approach just because it takes a few more MBs on disk or needs a few more KBs of memory is shortsighted, IMO. You can't always predict what a project will end up needing.
I'm legitimately asking: why would you use Flask instead of Django, even if you don't (but might) need the ORM, admin etc.? Thanks!
If you don't really know Django, the learning curve on Flask is a lot shallower. So if you don't need ORMs, models, admin etc. but you just want make those python scripts you wrote callable via a web browser then you'll get version 0.1 out the door a lot sooner with Flask.
Also a lot of Django kind of assumes the ORM as your data store. So if you need to use a primary data store that cannot be accessed via the ORM you lose a lot of what Django has to offer.
That being said, I agree that many 'simple' Flask projects have a tenancy to grow until you have implemented most of Django in Flask anyway.
Because Django is pretty badly designed, and it can get in the way when you need to do some more complex stuff. It get's the job done for 95% of cases, but that extra 5% can devolve into some pretty nasty stuff.
Typically the inflection for when Django becomes a nightmare is when you want to start splitting the application up. This is made really hard because Django depends on a whole load of globals (settings, urls, context preprocessors, the actual WSGI handler). With Flask you can have two separately configured applications running in the same process, routed with a bit of WSGI middleware.
This comes in handy when you need to do a 'soft' rewrite and have code running side by side. This also just makes things easier to test, because you can build a new application object for each test with it's own settings. You can't do that in Django with out a great deal of consternation; try creating two tests with different middleware stacks in Django, patching settings won't work because the application has already loaded, and there's no way to reload it between tests.
Also, for the same reason (as someone else said) it makes it tricky to use in non-web contexts. Like Celery.
Django's can't handle more complex database queries. If you've ever seriously used SQLAlchemy, you'll know what you're missing.
The ORM is also lacking a unit of work model, which can kill performance. The only way to create multiple objects in a single database round trip in Django is with `bulk_create`. But you can't create something like a group and then a user in a single database round trip. Sure you can create them in a single transaction, but that's not quite the same, because each roundtrip takes an extra millisecond. It's an easily avoided inefficiency that SQLAlchemy solves.
The template language is limited compared to Jinja2. While this is by design, it doesn't support streaming the response back. This is really annoying if you're generating massive templates. It would have also been really simple to do.
You can use whatever ORM or templating library you want. But at least if one day you realize you need something that Django offers out of the box it's there for you to use.
And if not it's not like it has any perceivable effect on performance.
As a rule of thumb, Flask is good when your app will only ever need to be a single file. Otherwise, as other commenters have said, you're basically reimplementing Django badly.
I feel the same way. I like simple frameworks like Flask, Starlette, or FastAPI. Django feels too heavy for me, though I get why people like it. Maybe I haven't learned it well enough, but I often feel like I'm having to do weird things or fight the framework to get done what I need. Simpler frameworks fit my mental model better.
I owe a lot to Django as it was really the thing that got me into backend programming by leading me to learn python in early 1.X days. My first real programming job was backend django programming for a newspaper. Eventually things shifted towards django-rest-framework, flask, and now my go-to Starlette. Haven't used Django in some time but really appreciate the project and happy to see it moving along.
Congrats on the new job! I never really dabbled with Go, but a team I was on recently worked with a backend written in Go, and I was present in lots of discussions :) For current project I'm using Starlette/asgi, but I'm sure Go would have some advantages
Can you elaborate? I've been using it for years and it mostly does what I want. It's an ORM and obviously sometimes generates unefficient queries I need to manually amend/refactor but otherwise I don't see any major issues with it?
In contrast I've looked at SQLAlchemy with Flask and I couldn't even wrap my head around declaring models (which inherit from the DB connection object if I remember correctly, so potential for circular imports) and manually managing transactions/sessions seems unnecessarily complex.
Comparing Django vs SQLAlchemy, Django's weakest point is its extremely static ORM syntax. The double underscore separation (e.g. user__group__name__icontains) for query parameters is likely my biggest pet peeve as it requires me to unroll unchecked strings together into a big garbled mess. I would have much appreciated being able to use more code-verbose query tools; the moment you have to go into building more dynamic user-defined query systems, Django's ORM just becomes a hindrance.
I also noticed the second part of the comment: Django does a lot of Python magic in the background through extensive use of reflection and class-level declarations, this hides away the abstractions (again) at the propose benefit of ease of use. And well, it is easy to use. For certain applications. I just personally found a lot of footguns trying to work with more advanced Postgres functionality (mainly around JSON) and it took a while to write my own abstractions atop the Django ORM to get to a comfortable level.
The inability to batch network calls in a single unit of work makes the ORM needlessly slow. That's also one of the main advantages to managing transactions/session yourself. It can save a huge number of round trips in a complex view.
The Django ORM itself is also slow at generating SQL. Recently I was bemused by a query, which took 500us to actually be generated. This wasn't doing anything particular wild, it just had a `select_related` call to an object that had about 50 fields (not great DB design, but beyond my control). The query actually took longer to build than it did to run.
That itself wouldn't be an issue if you could LRU cache the SQL query itself. But you can't, it's Django.
I doubt you're wrong. There's certainly a large set of problem that can be solved equally well by either framework. There are also projects where one is clearly a better solution.