I'm planning on learning Flask, but would love any thoughts on this from experienced users.
I'm planning on learning Flask, but would love any thoughts on this from experienced users.
You get a lot of stuff in the box with Django:
* a very functional (if exceptionally complicated and hard-to-modify) admin UI
* a friendly ORM with good support for autogenerated DB schema migrations (better than SQLAlchemy for simple CRUD, but harder to go off the beaten track when you need to)
And building on that, DRF gives you
* Autogenerated CRUD API endpoints for your DB models
* Autogenerated serializers for your DB models, with field type validation
All of the above exists as plugins for Flask, but the feature set is less well integrated, and you need to figure out which plugin you want to include.
If you're trying to build a super-light microservice then Flask is probably worth looking at, though you can get Django quite light with a bit of work.
If you're trying to build a more complex system with nested API endpoints then DRF makes this harder for you (it's not possible in native DRF, but there's a plugin for it; I'm using this approach so it's not terrible).
And if you don't buy in to Django's ActiveRecord ORM approach (e.g. you want to do DDD and want to use a DataMapper style interface on your DB), then you need SQLAlchemy, and you can't use all the clever ORM mapping tools that Django/DRF brings, and so you may as well use Flask.
As an exceptionally mediocre programmer who routinely creates extensive admin customisations I dispute this.
Seriously. Where does this meme come from? The admin is remarkably easy to customize for the most part. It's biggest flaws are incomplete documentation and a few monolithic methods/templates mostly around changelists.
from django.contrib import admin
from django.forms import Textarea
from .models import *
# Register your models here.
class BotTextInline(admin.StackedInline):
model = BotReplyContent
extra = 1
fieldsets = [
(None, {'fields': ('message_type', 'text', ('template_text', 'example_text',), 'order',)}),
('Actions and inputs', {'classes': ('collapse,'), 'fields': ('action_type', 'input_type',)})
]
# readonly_fields = ['input_type_ptr']
class BotReplyAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ('state', 'intent', 'context', 'description', 'next_state',)}),
]
formfield_overrides = {
models.CharField: {'widget': Textarea()}
}
list_display = ('state', 'intent', 'context', 'description',)
save_as = True # lets you save the content on screen as a new id
inlines = [BotTextInline]
admin.site.register(BotReply, BotReplyAdmin)
```Yes, you have to know what the class fields do. But you just saved yourself days of having to make your own CRUD view from scratch.
You're getting free authentication for an admin + free UI + a fully customizable list view + customizable create/update view + delete with a useful confirmation dialog + trivial field search + trivial filters of all sorts include date drill-downs.
It's a huge amount of things to do by hand.
I haven't found this myself to be honest. Usually you have to trawl through several stackoverflow posts and evaluate several different ways to do things you would expect to be simple. I don't have many examples in mind right now except showing image thumbnails, dealing with base64 image uploads and automatically resizing image uploads were way more cumbersome to implement than I would have expected. Most solutions we arrived at felt hacky for tasks you would think would be built in by now but I don't have a better framework in mind for CRUD work.
The default admin interface is really good though as well as the database migration tooling.
I've found the easy stuff to be very easy to do in the Django admin, and every time I've tried to do something off the beaten track, it's taken me days instead of the hours or minutes it feels like it should do.
Any of these would be good examples of things that feel like they should be easy, but aren't:
* Adding a link to a custom page onto the frontpage of the admin, ideally under a custom "App" header. * Creating a read-only view onto related objects (impossible in some situations, depending on the direction of the FKey relationship, annoying in others). * Presenting a form for creating nested one-to-one objects along with a parent.
I'm sure that if you routinely make modifications to the Django Admin it gets easier; the second time on any of these items is 10-100 times quicker for me.
I'd blame the poor code discoverability of the template-based system, poor documentation, and lack of a broad corpus of examples for making this harder than it could be.
This one's simple—just override base_site.html.
As for the others, I'm not really sure I understand what creating a read-only view / form for nested 1-1 objects means, but I'm sure there's a decent, not so crazy solution out there.
And if your changes break in a future version of Django, how exactly is anyone going to be able to debug that? Having to override admin templates that are at best only semi-documented is incredibly hacky.
But, if experience is any guide, despite lots of tinkering with the admin, Django has yet to break anything I’ve customized in the admin (I’ve been using Django since 0.96). YMMV.
In many years of working with Django I have not really seen a significant breaking change in the admin unless you override private methods.
The Django admin is meant for development. Ofc, it can be used by non-devs and I've seen a multimillion dollar business that mainly uses the builtin admin to manage their main product. However, there's always that point when customising something that is fundamentally different than what your needs are doesn't make sense.
I dispute this. And the community is pretty split on it too.
It is trivial to use serializers for non-ORM stuff. It is trivial to not use serializers at all. It is trivial to plug your own auth and permissions.
DRF doesn't like it when you do things that don't map to the models, and the documentation is pretty bad. I had to read the source in the end to figure out how to do what I wanted, and I got the sense that I was lucky it was doable.
If your database models map to your CRUD endpoints, though, everything is super easy.
I agree with your point that the further afield you go, the less you gain from the framework, though.
The flexibility of the admin UI has improved dramatically in the last few releases. It's still relatively complicated, but it now has way more places to override behavior.
By "last few" you mean "since about Django 1.4" in 2012
TL;DR - Flask for education and simple projects. Django for creating CRUD apps where you just need it to work.
Shameless plug -- I wrote this comparison[0] which shows how "Hello, World" is written in Flask and Django, and goes over some pros and cons of each in more detail. The editors added the "Why Flask might be better" part to the title, which caused most of the discussion about the post to turn into a flame war, but I tried to be as objective as possible (although I do prefer Flask -- another shameless plug for my book[1] Flask by Example).
[0] https://www.codementor.io/garethdwyer/flask-vs-django-why-fl...
For sure you can create a basic view with a ModelSerializer by hiding all the complexity, but very often it won't be enough to build what you want to build, and therefore you will have to search trough the documentation how things works.
In my opinion both Flask and Django are good for learning purposes, but Flask requires the user to handle more complexity than Django, which is not always the best thing if you want to learn
By using Flask first, you also end up thinking "wow, every time I build a flask app I go through these same repetitive steps. Would be nice if they were automated". Then you find they are automated in Django.
If you learn the solution first, it seems irritating and sometimes you never understand the problem that it's solving.
If you don't know what magic Django is doing for you, it feels restrictive to do stuff the Django way.
Mostly because the structure can be absolutely any shape, the last one I went onto used the Django structure, but it may as well have just been Django.
For a project, you can't blanket say "use flask" or "use django with DRF" without a little more context. They're not even that comparable in a way.
In terms of learning, if you already know how to code then just take a quick look at Flask. The core library is so small you can get the gist of it in less than an hour; there's nothing to lose. That's the great thing about Flask. After that you'll be fairly well on your own. There are libraries for most everything you need to do but you'll have to hack on them yourself.
If you have no dev experience then get a page running with flask just to get you excited about development...and then maybe stop there. You have to weigh up too many things and figure out how to get them all working nicely together to do more complex things. Django can guide you through this process better with the decisions made for you.
DRF is a really nice library. In fact, as someone sworn off django from the bad old (v0.9) days, I almost went back because DRF is so nice, and that was back when it was DRF v1. Tom Christie is an brilliant developer and if you're building an api, DRF is best of breed.
Certainly it took me a bit longer to get up and running compared to if I'd just used DRF, but on the plus side, I feel like I have much stronger grasp on how everything works now, whereas Django still has a ton of surface area that feels a bit like magic to me. If you're interested, the repo is open sourced under MIT here[0].
Normally not a "big" framework kinda dev but I'm quite proud of how clean and well organized the resulting code was. Adding additional endpoints in the future can be done in minutes or hours at most, since all the pieces are now there.
I don't have any experience beyond that though, and I've never started a brand new product from the ground up with it, but it made my life super easy last week.
Django on other hand will give you monolithic solution for the price of less flexibility than flask/pyramid.
If you just want to handle REST views - I think falcon framework is nice (speed wise) - haven't used it myself yet though.
I use both, for very different applications.
There are also some packages for Django that make it more flask-like (e.g. importd) but I've never used them in real life.
I used sqlalchemy a little bit in 2012 and didn't like the feel of it (too much configuration over convention, I felt), and since then I've been using Django's ORM even in non-Django projects (which is great both because of how good Django's ORM is and because eventually I always encounter the for need a nice admin interface and solve it in 10 minutes with Django's admin panel generator).
SQLA handles pretty much everything. It's built in really clean layers with the Core implementing all of the sql abstractions. If you can write it in sql, you can write it in SQLA. The ORM is just another abstraction which sits on top of that. This architecture makes it really extensible and flexible. We've bent Alembic (the migration tool) to our will and it's amazingly easy to hack on. For me it's one of the greatest pythonic tools.
One of the deeper more subtle issues is that if you architect using something like the django orm, it's assumed to be there and all the libraries / plugins etc will have a structure that's insidiously tied to the underlying data loader (this is a django problem, not a django orm one). Depending on what you're doing that might be ok, but for some projects that's going to bite you over the lifetime of the project.
I always thought every project should have a few well thought out and relevant "invariants" - assumptions that it's easier to rewrite than change, that the whole architecture is built around. They can be a data structure, a library, a way data flow is structured, whatever, as long as they make the rest of the architecture naturally fall into place around them.
On database-heavy projects like CRUD web-apps it makes a lot of sense for the ORM to be such an invariant. And I've never hit the point where I was sad to have my ORM so entangled with my code. So it's really hard for me to get this perspective, although I hear it so much around me.
I guess my takeaway from this conversation would be to study the SQLA architecture as a shining example of correct layers of abstraction, but keep using Django ORM in practice until I encounter a situation where I wish I wasn't.
How did you "bend the migration tool to your will"? What did you need it to do that it didn't do natively? What other things did you need to do that an opinionated generic ORM like Django's considered "against the grain"?
Alembic works by introspecting the db (using sqla) and then creating a script, in sqla, that migrates the current db to match the model defined in code. You can hook in to change that process. In our case, we wanted to create a fully automated Choice type using native python enums. We then added a hook to alembic so it could detect the inconsistency in between the constraints in the db and the code and update them accordingly. The whole thing is about 50 lines. There was a bit to learn, but with sqla / alembic you just slowly start peeling back the layers. Ironically that's all probably something that django has covered!
edit: To make this a more-effort post see https://www.sqlalchemy.org/features.html and http://www.aosabook.org/en/sqlalchemy.html for reasons.
One disadvantage is you can't use wsgi middleware, but after rewriting our middleware to components they ended up being much neater. I'm a fan of the bottom up approach where you define the components you want in a route. We have some routes which just returns the database query and with a custom renderer it will get rendered in the json format we expect