5 ms·
I am convinced (through experience) that overuse of the Django ORM (and Django REST Framework, which is tightly coupled to the ORM) is a pattern that leads to i
by skrtskrt 6y ago
I am convinced (through experience) that overuse of the Django ORM (and Django REST Framework, which is tightly coupled to the ORM) is a pattern that leads to intractable ball-of-mud apps.
There's so much stuff available for Django out of the box like auth, http/REST, CSRF, etc that it makes it really hard to justify not using Django at the start of a project. But the recommended/accepted Django patterns lead to pure misery down the road due to how coupled the app becomes.
You can make a well-architected Django app, it just requires ignoring almost every common or recommended Django pattern.
- scrollaway 6y ago> it just requires ignoring almost every common or recommended Django pattern Indeed I'm already ignoring a lot of them. No forms or templates of any kind, nor any of the stuff that is built for that (messages framework etc); only using DRF; stick to Postgres-only (I don't make much use of the db abstractions), etc. The ORM is … nice, and has its limitations. I really just want to start using FastAPI by default but I'm concerned I'm setting myself up for a lot of busywork by skipping on such high-value niceties as the Django admin or DRF viewsets.
- ttymck 6y agoI think DRF Viewsets may be easy to replicate with the help of class based views from fastapi-utils[0] But I agree that Admin is still the missing killer feature from the fastapi ecosystem. But it looks like there is an emerging ORM/Admin combo heavily based on Django[1]. I've also considered (if you prefer SQLAlchemy) the possibility of instantiating a Flask/Flask-Admin wsgi app and mounting it on a running fastapi server, but haven't been able to confirm if that works yet. (EDIT: I just confirmed it does indeed work, and is rather painless.) [0] https://fastapi-utils.davidmontague.xyz/ https://fastapi-utils.davidmontague.xyz/ [1] https://github.com/long2ice/fastapi-admin https://github.com/long2ice/fastapi-admin
- collyw 6y agoMaybe I am just a god at writing Django, but I don't write ball of mud apps.
- iainctduncan 6y agoSo true. I've seen them too much. The SQLAlchemy data mapper is so much better.
- leetrout 6y agoOne of the worst pieces of advice leads to over packaging into apps. Once you start making foreign keys across apps (especially ignoring content types as the connective tissue) you’re headed to a path of needless complexity. Lots of people have learned Python and Django together much like rails and Ruby but they don’t immediately grok that models, views, etc can be packages with multiple modules and apps aren’t the only (nor the correct) abstraction for code organization.
- raverbashing 6y ago> but they don’t immediately grok that models, views, etc can be packages with multiple modules Ah yeah but the MVC/MVT/"SOLID" theologians don't like that
- neillyons 6y agoI prefer using packages over apps too. My rule of thumb is, use packages unless the code is reuseable, then it can be an app, but if the code is reuseable it makes no sense for it to be in my repo, so should be extracted to an external repo on pypi.
- gabereiser 6y agoMy experience backs the ball of mud claim. Large multi “app” Django stack with no clear architecture or direction. Everything’s a model. It was a nightmare adding anything new.
- theptip 6y agoI strongly agree with this view. Some things in particular that I think become anti-patterns as your Django app grows: 1. Everything is coupled to the ORM models / QuerySet, so any part of your stack can mutate the request's query. On one hand this is great for things like dynamic APIs and adding arbitrary filter params, but it's really hard to keep your concerns cleanly separated. (You could use Django Seal to work around the QuerySet mutability issues but it requires finesse). 2. The ActiveRecord pattern of calling model.save() to write to the DB is actually really restrictive; it forces you to conflate DB logic with your business logic, where the former often more naturally spans multiple domain models (i.e. your DB session logic should be at the application layer while your business logic is at the domain layer, in DDD terminology). The NHibernate / SQLAlchemy pattern of tracking dirty writes and only making DB requests when you call Session.flush() / Session.commit() is much more flexible. An example is if you have an object with a list of other objects, you might want to write some code in a transaction like "for day in week: parent.children.add(calculate_child_for(day))". In Django you'd be saying `parent.child_set.add(...)` which immediately runs a DB query. If your logic fails on the last child, you don't need to have sent the earlier DB requests. So instead you end up having to contort your business logic to avoid DB side-effects, collecting a list of unsaved items to later save. Using dirty-tracking means you can run your algorithm in "plan mode" / no side-effects by just omitting the "Session.commit()" at the end; no spurious DB calls will be made. 3. The ActiveRecord pattern of writing "model.fieldname = foo" and having every part of the stack treat your objects like CRUD data dictionaries completely breaks encapsulation; if you're trying to write proper domain models that encapsulate your business logic, you don't want everybody to be able to poke arbitrary state. You end up having to guard against everything being in arbitrarily bad states, because every field is public. In my experience it's much easier to test and less confusing to make most class members private, and have well-defined state-transition methods. But making your model members private in Django is obnoxiously verbose and you're swimming against the current at every step; the whole system is based on the assumption that your API is CRUD; you need to say something like `_owner_name = CharField(db_column='owner_name', verbose_name='owner name', ....)` in order to avoid borking your DB schema and admin pages. 4. Django apps are a trap. The tutorials suggest you should expect to create multiple apps but this will make your codebase a pain to refactor; you can't migrate models between apps, and so you're stuck with the first app structure you go with. Just use a single app `core` and pretend that apps are not a thing, unless you are implementing a completely standalone composable app in another repository. 5. Django is in general more on the "config over convention" end of the spectrum than, say, Rails. But it still has some annoying magic like requiring all your model files to be imported in the `appname.models` module. This means you need to do wacky re-importing in `appname/models/__init__.py` if you want to avoid having all your models in one file. Why not just allow us to configure the model path(s) like we can configure template and other paths? Having said all this, I agree with you that Django is really productive for the early phases of a project. When I started working in Flask I had to spend a few days figuring out each of a large number of things that are just batteries-included in the Django framework. So I can see the appeal, and for small side-projects I'd still probably reach for Django. But I think I'd probably start from Flask or FastAPI for new startups.