Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Overcoming Troubles with Class-Based (Generic) ...

Andrew Pinkham
September 05, 2013

Overcoming Troubles with Class-Based (Generic) Views

An introduction to Class-Based Views and Class-Based Generic Views. Talk given at DjangoCon 2013.

Andrew Pinkham

September 05, 2013
Tweet

More Decks by Andrew Pinkham

Other Decks in Technology

Transcript

  1. # bank/views.py class AccountList(View): def get(self, request): acct_list = Account.objects.all()

    return render(request, 'bank/account_list.html', {'account_list': acct_list}) class AccountDetail(View): def get(self, request, slug): acct = get_object_or_404(Account, slug=slug) return render(request, 'bank/account_detail.html', {'account': acct})
  2. # bank/views.py class AccountCreate(View): def get(self, request): form = AccountForm()

    return render(request, 'bank/account_form.html', {'form': form}) def post(self, request): form = AccountForm(request.POST) if form.is_valid(): new_acct = form.save() return redirect(new_acct) else: return render(request, 'bank/account_form.html', {'form': form})
  3. # bank/views.py class TransactionCreate(View): def get(self, request): form = TransactionForm()

    return render(request, 'bank/account_form.html', {'form': form}) def post(self, request): form = TransactionForm(request.POST) if form.is_valid(): new_acct = form.save() return redirect('bank_account_list') else: return render(request, 'bank/account_form.html', {'form': form})
  4. # bank/views.py class TransactionCreate(View): def get(self, request): form = TransactionForm()

    return render(request, 'bank/account_form.html', {'form': form}) def post(self, request): form = TransactionForm(request.POST) if form.is_valid(): new_acct = form.save() return redirect('bank_account_list') else: return render(request, 'bank/account_form.html', {'form': form})
  5. # bank/urls.py from django.conf.urls import patterns, url from .models import

    Account from .views import AccountList, AccountDetail, \ AccountCreate, TransactionCreate urlpatterns = patterns('', url(r'^account/$', AccountList.as_view(), name='bank_account_list'), url(r'^account/create/$', AccountCreate.as_view(), name='bank_account_create'), url(r'^account/(?P<slug>[\w\-]+)/$', AccountDetail.as_view(), name='bank_account_detail'), url(r'^transaction/$', TransactionCreate.as_view(), name='bank_trans_create'), )
  6. # bank/views.py class PostFormMixin(object): ... def get(self, request): form =

    self.form() return render(request, self.template, {'form': form}) def post(self, request): form = self.form(request.POST) if form.is_valid(): new_obj = form.save() if self.redirect: return redirect(self.redirect) else: return redirect(new_obj) else: return render(request, self.template, {'form': form})
  7. # bank/views.py class AccountCreate(PostFormMixin, View): form = AccountForm template =

    'bank/account_form.html' class TransactionCreate(PostFormMixin, View): form = TransactionForm template = 'bank/account_form.html' redirect = 'bank_account_list'
  8. # bank/views.py class PostFormMixin(object): ... def get_template(self): if self.template ==

    '': raise ImproperlyConfigured( '"template" variable not defined in %s' % self.__class__.__name__) return self.template
  9. # bank/views.py class PostFormMixin(object): ... def get_redirect_url(self,obj): if self.redirect: url

    = self.redirect else: try: url = obj.get_absolute_url() except AttributeError: raise ImproperlyConfigured( '"redirect" variable must be defined ' 'in %s when redirecting %s objects.' % (self.__class__.__name__, obj.__class__.__name__)) return url
  10. # bank/views.py class PostFormMixin(object): ... def get(self, request): form =

    self.form() return render(request, self.get_template(), {'form': form}) def post(self, request): form = self.form(request.POST) if form.is_valid(): new_obj = form.save() return redirect(self.get_redirect_url(new_obj)) else: return render(request, self.get_template(), {'form': form})
  11. # bank/views.py from django.core.urlresolvers import reverse_lazy from django.views.generic import (ListView,

    DetailView, CreateView) from .models import Account, Transaction class AccountList(ListView): model = Account class AccountDetail(DetailView): model = Account class AccountCreate(CreateView): model = Account class TransactionCreate(CreateView): model = Transaction success_url = reverse_lazy('bank_account_list')
  12. from django.conf.urls import patterns, url from django.core.urlresolvers import reverse_lazy from

    django.views.generic import (ListView, DetailView, CreateView) from .models import Account, Transaction urlpatterns = patterns('', url(r'^account/$', ListView.as_view(model=Account), name='bank_account_list'), url(r'^account/create/$', CreateView.as_view(model=Account), name='bank_account_create'), url(r'^account/(?P<slug>[\w\-]+)/$', DetailView.as_view(model=Account), name='bank_account_detail'), url(r'^transaction/$', CreateView.as_view(model=Transaction, success_url=reverse_lazy( 'bank_account_list')), name='bank_trans_create'), )
  13. Feature Changes List Accounts and Transactions in AccountList Split TransactionCreate

    Send To link Send From link Generate Slug Automatically
  14. @classonlymethod def as_view(cls, **initkwargs): for key in initkwargs: if key

    in cls.http_method_names: raise TypeError('...') if not hasattr(cls, key): raise TypeError('...') def view(request, *args, **kwargs): ... update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) return view
  15. def as_view(cls, **initkwargs): ... def view(request, *args, **kwargs): self =

    cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs)
  16. def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler

    = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
  17. http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace'] def

    dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs)
  18. CBGV TemplateView RedirectView DetailView ListView FormView CreateView UpdateView DeleteView ArchiveIndexView

    YearArchiveView MonthArchiveView WeekArchiveView DayArchiveView TodayArchiveView DateDetailView
  19. # bank/views.py class AccountList(ListView): model = Account def get_context_data(self, **kwargs):

    context = super(AccountList, self)\ .get_context_data(**kwargs) context['transaction_list'] = Transaction\ .objects\ .order_by('-date')\ [:10] return context
  20. from django.forms import ModelForm from django.utils.text import slugify from .models

    import Account class AccountForm(ModelForm): class Meta: model = Account def clean(self): cleaned_data = super(AccountForm, self).clean() name = cleaned_data.get("name") slug = cleaned_data.get("slug") if not slug and name: cleaned_data['slug'] = slugify(name) return cleaned_data
  21. from django.forms import ModelForm from django.utils.text import slugify from .models

    import Account class AccountForm(ModelForm): class Meta: model = Account exclude = ('slug',) def clean(self): cleaned_data = super(AccountForm, self).clean() name = cleaned_data.get("name") slug = cleaned_data.get("slug") if not slug and name: cleaned_data['slug'] = slugify(name) return cleaned_data
  22. class AccountForm(ModelForm): class Meta: model = Account exclude = ('slug',)

    def save(self, commit=True): instance = super(AccountForm, self)\ .save(commit=False) instance.slug = slugify( self.cleaned_data\ .get('name', '')) if commit: instance.save() self.save_m2m() return instance
  23. class TransactionCreate(CreateView): ... def dispatch(self, request, *args, **kwargs): slug =

    kwargs.get('slug', None) account_obj = get_object_or_404(Account,\ slug__iexact=slug) self.acct_name = account_obj.name self.acct_pk = account_obj.pk self.acct_url = getattr(account_obj,\ 'get_absolute_url', '') return super(TransactionCreate, self)\ .dispatch(request, *args, **kwargs)
  24. class TransactionCreate(CreateView): ... def get_initial(self): initial_data = super(TransactionCreate, self)\ .get_initial()

    if self.form_class == TransferFormFrom: initial_data['from_account'] = self.acct_pk elif self.form_class == TransferFormTo: initial_data['to_account'] = self.acct_pk else: raise ImproperlyConfigured( '"form_class" variable must be defined 'in %s for correct initial behavior.' % (self.__class__.__name__, obj.__class__.__name__)) return initial_data
  25. class TransactionCreate(CreateView): ... def get_context_data(self, **kwargs): context = {} context['account_name']

    = self.acct_name if self.form_class == TransferFormFrom: context['trans_dir'] = 'From:' elif self.form_class == TransferFormTo: context['trans_dir'] = 'To:' else: raise ImproperlyConfigured( '"form_class" variable must be defined 'in %s for correct initial behavior.' % (self.__class__.__name__, obj.__class__.__name__)) context.update(kwargs) return super(TransactionCreate, self)\ .get_context_data(**context)