9 ms·
Django Views – The Right Way
- BiteCode_dev 3y agoOk I was ready to hate this article the second I read the title, and I clicked on it just to get enough ammunition to crap on it. But it's actually quite good and I came to similar conclusions after trying to make CBV work for years. Plus it's well written. Damn.
- scrollaway 3y agoThere's a lesson to learn, here.
- firecall 3y agoAs a Rails dev that is having to work with Django lately, I'll give it a read. The Django fat models and (lack of) code structure in the Django project I'm working on fixing is painful for me!
- adamckay 3y agoI'd agree that CBV's can be difficult to reason with (though I personally still use them for most views) but the following two websites have massively helped me make sense of them. The only thing missing really is the actual flow of a request, such as knowing `dispatch() -> get() -> get_context_data()` but it's not terribly difficult to work out and learn. https://ccbv.co.uk/ https://ccbv.co.uk/ https://www.cdrf.co/ https://www.cdrf.co/
- Nextgrid 3y agoFor anyone struggling with remembering class based view mixins, I suggest you get yourself a proper IDE that allows you to trivially look into the implementation of anything as well as lookup any symbol with fuzzy search. PyCharm/IntelliJ is one. Since using such an IDE I pretty much never have to rely on the documentation because it’s much easier to Cmd+click into a symbol and look at its implementation.
- kodah 3y agoProperly configured VSCode with the Python Virtual Environment Manager plugin also makes this easy.
- darkteflon 3y agoIs “Python Virtual Environment Manager” a third party plugin? I had a search but could turn nothing up. Wondering if I’m missing out.
- daydream 3y agoThey probably mean this one: https://marketplace.visualstudio.com/items?itemName=donjayamanne.python-environment-manager https://marketplace.visualstudio.com/items?itemName=donjayam...
- nouveaux 3y agoThe problem with relying on fancy IDE to untangle your classes is that it breaks on GitHub. Also it's harder to discuss snippets of code asynchronously. People like to think CBV offer a lot of free things but they miss the trade off. CBV makes it easier to write code but harder to read the code. CBV should only be used for the simplest of cases IMO.
- code_biologist 3y agoI came here to post that first link. I don't mind working with CBVs if that's the existing style in a project, but the very existence of CCBV highlights the pain of lots of indirection. Certainly a lot of power, but not trivial to remember how everything connects either.
- sergioisidoro 3y agoThe most important caveat that the author mentions: this advice may not apply to Django Rest Framework. Class based views for DRF are awesome because they represent a resource. It has been a great experience writing REST APIs with class based views, and it reduces boilerplate with a tolerable amount of implicit logic. But as soon as I need to render a simple html page from a request, I share the frustration of the author.
- pdhborges 3y agoI read the article and I'm left scratching my head why this advice doesn't apply to DRF. The DRF Generic ViewSets and Views provide maximum magic plus they also shift part of the creation process into Model Serializers. Doing anything custom with this setup involves overriding bunch of methods and chasing their original definitions through an handfull of mixins.
- rpep 3y agoCBV doesn't require you to do anything in particular other than implement retrieve/list/etc. methods on the GenericViewSet as needed, and get/post/etc. on GenericAPIView. You can use the convenience methods in Django and mixins, but as soon as you get beyond a simple case, they start to become a hindrance. The only thing beyond that that I'd recommend is specifying a get_serializer_class method since it simplifies your boilerplate and plays well with drf-spectacular for generating your API documentation. I generally don't use ModelSerializer unless the logic is very simple, I've been meaning to write a blog post for a long while about avoiding it when your data model is split across two database tables.
- pdhborges 3y agoIn my current codebase there is a lot more overriding going on than simply implementing the retrieve/list/etc... methods. On a quick review I found overrides for: - get_object - get_serializer_class - perform_create - get_queryset - get_serializer_context - perform_destroy
- kolanos 3y agoFunction based views are good until you have to handle many request methods, then they become a hideous series of if/elif/else.
- notagoodidea 3y agoOr a good opportunity for match/case if >3.9.
- mastazi 3y ago> series of if/elif/else in my personal experience this can also happen when using class based. It depends on how much your application is "CRUD-y" with a CRUD, it's easier to go CBV and have minimal branching, and use the different HTTP verbs based on the CRUD operations. but sometimes you have apps where you have a bunch of different operations, and maybe all of those operations are just types of updates to the same type "Foo". in that case, FBVs work best, because you can just do def update_foo_expiry() def renew_foo() def rename_foo() etc. etc. - all of the above would have ended up being if/elif/else in a put() method in a CBV
- ehutch79 3y agoIf you would have made them separate function views, they should be separate class views.
- jerrygenser 3y agoOr just use django-ninja if you are writing an API. Maybe it's just because I came from teams that used tornado and then fastapi but when I started using django which was about 8 months ago, I simply grabbed django-ninja and haven't thought much about it. It seems like everything in this article would be solved by using a simpler interface for writing endpoints with body parsing, output schemas, and url parsing. https://django-ninja.rest-framework.com/ https://django-ninja.rest-framework.com/
- winrid 3y agoThe article isn't really about APIs. If you're writing APIs, most people will reach for DRF. Ninja looks neat, though.
- xwowsersx 3y agoHow does Django Ninja compare to Django Rest Framework (DRF)? Is there much of a difference in terms of performance?
- jerrygenser 3y agoDjango ninja performs better primarily due to relying on pydantic for parsing and validating requests. In a high throughput scenario that's going to be the primary CPU bound area. However usually network or file io is going to be a limiting factor far before CPU. Django ninja also is build with async in mind fron the start so immediately opens up possibilities that are not possible with DRF. Seems like as of 4mo ago the maintainers of DRF added async support via a separate module.
- xwowsersx 3y agoThanks very much for the reply. I actually had not seen that DRF added async support. I must take a look at that! I'm very curious to know how big of an impact switching to AsyncViews would have. Any experience on that front?
- xwowsersx 3y agoI haven't tested very thoroughly yet, but I'm seeing large improvements in latency and throughput by switching to async views!
- whakim 3y agoI agree with the author about 99% of what's written here. Except for the preference for thin views (i.e. thin controllers and fat models). It seems to be me that the author goes to great lengths to decry the abstractions inherent in CBVs (and other patterns) which hide logic from the user and make code harder to read all in the name of "DRY". Except that pre-emptively moving your queryset methods into another model does exactly that! Sure, if you're repeatedly doing some super-complicated joining and filtering - by all means move your logic elsewhere. But my experience with this pattern (mostly in Rails codebases which seem to love it) has been tons of extra functions scattered around models that don't actually get used every time and often themselves become out-of-date.
- dec0dedab0de 3y agoI like class based views. But either way if your views are getting complicated you're doing it wrong. Views should just be plumbing to glue together the data you want for the template. All your business logic should be methods on your models, and if those are getting too complex they should be importing separate modules. The views should mostly be boilerplate. The reason everything should be on the model (or atleast a separate module if you're a functional zealot) is so you can have your same logic available in your async queue, the api, cron jobs, and the shell for troubleshooting. If you have any logic tied up in the view it's a huge pain to access from anywhere else. And I don't know about you, but I always end up needing the logic somewhere else.
- kamikaz1k 3y agoThere should be an intermediate layer between views and models, especially if they're getting complicated. Complications probably come from coordinating more models. So you introduce a Service layer. So Models house logic specific to handling the data within that Model. And Services can orchestrate across multiple Models, or Multiple Services. But you try to keep it as a DAG. There's always room for pragmatic compromise though...
- dec0dedab0de 3y agoI tend to handle that with an extra model that just has relationships to the other models and some methods to make it work
- gmt2027 3y agoThere's an argument against service layers in Django. The recommendation is to use instance and manager methods. https://www.b-list.org/weblog/2020/mar/16/no-service/ https://www.b-list.org/weblog/2020/mar/16/no-service/
- btown 3y agoOver time, though, this approach leads to massive model/manager files with dozens of groupable methods. Sometimes a service is a useful abstraction as a way to encourage placing related functionality in a dedicated file. The one-minute learning curve for telling new team members what that services directory does, may save hours that would have otherwise been necessary to grok a thousand-line model file.
- jgalt212 3y agoThe Django ORM is close to useless if your database is not generated by the web app.
- Nextgrid 3y agoYou can “cheat” by having fake models backed by database views. Declare your view creation/deletion as SeparateDatabaseAndState migrations (with RunSQL operations creating the actual views). It’s not scalable long-term, but if your objective is to either slowly migrate those views to true Django models, or for few, specific tables, then I think it’s a good workaround.
- ehutch79 3y agoIf you’re not all in on the Orem I’m not sure I’d recommend django
- ehutch79 3y agoOn mobile. I meant ORM
- jgalt212 3y agoIf you have a database-driven web app, and you like Python, what would use then?
- ehutch79 3y agoProbably something like flask/sql alchemy. It’s been a bit since I’ve looked at the field. But Django is definitely opinionated on things
- 7373737373 3y agoIf it's a simple app, check out this flask+dataset(sqlite) approach: https://github.com/void4/lazyweb/ https://github.com/void4/lazyweb/
- andybak 3y agoThis doesn't match my experience.
- evrimoztamur 3y agoI'm not so sure about the 'including data in your template' section. It shows the regular way of adding context fields directly. I found greater success and clarity with the @property and its cached brother @cached_property decorators. Using these two, you can access the view instance and its properties via just, e.g., 'view.foo' in the template. @cached_property from what I understand is in fact originally a Django creation too (or at least its first popularising use https://github.com/python/cpython/issues/65344 https://github.com/python/cpython/issues/65344).
- willmeyers 3y agoI think this is a great resource. The only comment I have is on the Thin Views chapter. Instead of attaching logic to the models, I like to make a services.py file in my app that has functions that satisfy all sorts of business logic that I can plop in anywhere I need them. Here's another opinionated Django guide: https://github.com/HackSoftware/Django-Styleguide https://github.com/HackSoftware/Django-Styleguide if anyone's interested
- noisy_boy 3y agoThis is how I do things in Spring Boot too. Model specific stuff stays in the model, cross model business logic goes into services.
- kisamoto 3y agoHaving worked on Django projects of multiple sizes I really like the HackSoftware Django Styleguide. It was a refreshing approach rather than tracking down logic scattered between models, views, managers etc. For very simple projects it can add a bit of bloat/boilerplate but as complexity grows it's great to have reusable selectors & service functions to use.
- IgorPartola 3y agoThis is a great guide. Some thoughts having been working with Django for a while: FBV are superior in most cases. Less magic, just code. CBV are better when you have complex operations. Not everything is just CRUD, and sometimes you do need a long POST method and a long GET. Sometimes the two share a bunch of functionality that makes sense to break out into its own function(s) but that functionality clearly belongs in the view layer. Recently I worked on a system where my models all shared a fairly complex system of being versioned and having start_on and ends_on dates to make them temporal. In fact each concept had two models: a root that most other models refer to and a version. While the dozen models were all different the logic of processing creating and updating these models instances was the same between all of them but each model had its own unique additional rules for how to process them. So I created a pair of base CBV (one for a listing and one for a single object) that implemented CRUD methods for them and then each object got its own CBV pair based on those. I tried this with FBV and it was much messier, especially with form validation shared between POST and PUT. Sometimes a complicated setup like this requires CBV to create neat and concise code (relatively speaking). On moving logic to the model layer: my litmus test for this is whether the logic is used more than once. I’d you have a view that fetches model instances based on a complex filter (dates, user permissions, etc.), but it literally is the only one of its kind, why spread logic out though model code? But if you have either multiple views that do this, or more likely views that share logic with management commands and/or background tasks then it absolutely makes sense. Service layers leak abstractions. Django doesn’t support this as a first class citizen, so why should you? You won’t be able to switch to a different framework just by rewriting what your service layer talks to so just don’t do it. DRF is a CPU hog even at mild loads. I use a home grown model-to-JSON serializer that is quite a bit faster and Django forms to validate input.