Open cipang opened 4 years ago
Thanks for the question!
I agree that there is a certain kind of neatness to the CBV pattern you've highlighted. My practice is to write the code exactly as you have here in the second example. I try to keep the contents of each branch small, by moving logic into the model layer or into a Form
or utility where appropriate.
The advantage of this is that common setup code between get
and post
becomes much easier - you can just re-use local variables. This to me is a big advantage, since there is almost always common logic, and communicating by setting variables on self
is kind of ugly and harder to reason about.
The disadvantage is that you get a longer function, instead of several shorter methods. I don't worry about this too much, because I don't pay much notice to people who say "no function should be more than 5 lines long" etc. I prefer short function/methods when they are meaningful bits of logic, but not for the sake of it.
In terms of boilerplate, it's about the same.
If there isn't common logic, I might have 2 completely separate views and use require_POST
and require_GET
decorators. https://docs.djangoproject.com/en/3.1/topics/http/decorators/#django.views.decorators.http.require_POST
For the case where you have no common setup, but want the same URL, an interesting possibility might be to do something like this:
booking_view = dispatch_by_http_verb()
@booking_view.get
def booking_view(request):
# do get thing
@booking_view.post
def booking_view(request):
# do post thing
Or something similar. See https://docs.python.org/3.8/library/functools.html?highlight=singledispatch#functools.singledispatch for other inspiration on what it might look like.
Implementation of dispatch_by_http_verb
is left as an exercise BTW! I'm not sure if I'd use this pattern but it might be worth exploring.
Here's another idea:
@dispatch_by_http_verb
def booking_view(request, booking_id):
# Common setup
booking_account = get_booking_account_from_request(request)
booking = Booking.objects.for_account(booking_account).get(id=booking_id)
return (booking_account, booking)
@booking_view.get
def booking_view(request, booking, booking_account):
# do get thing
@booking_view.post
def booking_view(request, booking, booking_account):
# do post thing
Here dispatch_by_http_verb
takes the return args from the first function and passes them as parameters to the sub view functions. Perhaps a bit obfuscated?
Of course, if you really like the CBV pattern you've described, then I'd suggest you just use it :-)
I had the same question and felt like this topic was conspicuously absent from the guide. I think the discussion in this thread would be a great addition.
Many thanks for your informative guide first of all.
One of the way I use CBV is to group related GET/POST/HEAD... code together. For example:
instead of using a bunch of if statements in a FBV like this:
What's your opinion on that? Any suggestions in this use case can be included in your guide?