Design by minimalism
Tom Christie, DabAPPs
https://github.com/tomchristie
https://twitter.com/_tomchristie
Django Vanilla Views
-
Aim to provide the exact same set of functionality as existing GCBVs.
-
But with a simplified API and implementation.
Simple Class Hierarchy
View --+------------------------- RedirectView | +-- GenericView -------+-- TemplateView | | | +-- FormView | +-- GenericModelView --+-- ListView | +-- DetailView | +-- CreateView | +-- UpdateView | +-- DeleteView
Less magical behavioUr
Cascading set of options.
- `.template_name` attribute on the view.
- `.template_name_field` set on the `.object`.
- `{app}/{model}{suffix}.html` based on the `.object`.
- `{app}/{model}{suffix}.html` based on the `.model`.
-
Configuration error.
Explicit Template name or fall back to default.
-
`.template_name_field` set on the `.object`.
-
`{app}/{model}{suffix}.html` based on the `.model`.
-
Configuration error.
More DIRECT Style
Form DATA POPULATED INDIRECTLY
def get_form_kwargs(self): # ... if self.request.method in ('POST', 'PUT'): kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES, }) return kwargs
Form data populated directly
form = self.get_form(request.DATA, request.FILES)
LESS API
Let's remove all these
- initial
- prefix
- get_initial()
- get_prefix()
- get_form_kwargs()
Just OVERRIDE `get_form()`
def get_form(self, data, files, instance=None): user = self.request.user return AccountForm(data, files, user=user, instance=instance)
Let's remove all these
- paginator_cls
- paginate_orphans
- get_paginate_orphans()
Just OVERRIDE `get_paginator()`
def get_paginator(self, queryset, page_size): return CustomPaginator(queryset, page_size, orphans=3)
The network effect of constraints
- Aim for a small set of often repeated patterns.
- So we can all follow the well-trodden path.
The complexity feedback loop
- Introducing extra API makes the implementation a little more complex.
- In turn it becomes a little less obvious how to simply override the the behaviour.
-
Future users will tend to request extra API to make their lives easier.
In particular...
"Write your own class based views"
The Miuse of super
Calling super can obfuscate
what's actually happening.
Instead of this...
def get_context_data(self, **kwargs): context = super(MyView, self).get_context_data(**kwargs) context['user'] = self.request.user return context
Write this...
def get_context_data(self, **kwargs): kwargs['object'] = self.object kwargs['user'] = self.request.user return kwargs
Instead of this...
def form_valid(self, form): send_activation_email(self.request.user) return super(AccountActivationView, self).form_valid(form)
Write this...
def form_valid(self, form): send_activation_email(self.request.user) form.save() return HttpResponseRedirect(self.success_url)
Why mixins Rarely make A good API
Instead of this...
class MyView(LoginRequired, View): # ...
Aim for this...
class MyView(View): permissions = (LoginRequired,)
Design by COMMITTEE
CAN tend to COMPLEXITY
Be an advocate for design restraint.
API as education
-
The CBVs are our bread and butter.
- Recognise the knock on effects of our design choices.
The end result
Instead of this style...
class AccountCreateView(CreateView): model = Account form_class = AccountForm def get_success_url(self): return self.object.account_activated_url() def get_form_kwargs(self): kwargs = super(AccountView, self).get_form_kwargs() kwargs['owner'] = self.request.user return kwargs def form_valid(self, form): send_activation_email(self.request.user) return super(AccountCreateView, self).form_valid(form)
Simpler, More Direct
class AccountCreateView(CreateView): model = Account
def get_form(self, data=None, files=None, instance=None):
owner = self.request.user
return AccountForm(data, files, instance=instance, owner=owner) def form_valid(self, form): send_activation_email(self.request.user) account = form.save() return HttpResponseRedirect(account.account_activated_url())
Thanks
https://github.com/tomchristie/django-vanilla-views
@_tomchristie
Design by minimalism
By tomchristie
Design by minimalism
- 5,300