from braces.views import FormMessagesMixin from django.conf.urls import url from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from django.views.generic import (CreateView, DeleteView, DetailView, ListView, UpdateView) from crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display def from_to(start, end): return list(range(start, end + 1)) def make_pagination(index, num_pages): '''Make a list of adjacent page ranges interspersed with "None"s The list starts with [1, 2] and end with [num_pages-1, num_pages]. The list includes [index-1, index, index+1] "None"s separate those ranges and mean ellipsis (...) Example: [1, 2, None, 10, 11, 12, None, 29, 30] ''' PAGINATION_LENGTH = 10 if num_pages <= PAGINATION_LENGTH: return from_to(1, num_pages) else: if index - 1 <= 5: tail = [num_pages - 1, num_pages] head = from_to(1, PAGINATION_LENGTH - 3) else: if index + 1 >= num_pages - 3: tail = from_to(index - 1, num_pages) else: tail = [index - 1, index, index + 1, None, num_pages - 1, num_pages] head = from_to(1, PAGINATION_LENGTH - len(tail) - 1) return head + [None] + tail class BaseCrudMixin(CrispyLayoutFormMixin): @property def namespace(self): return self.model._meta.model_name def resolve_url(self, url_name, args=None): return reverse('%s:%s' % (self.namespace, url_name), args=args) @property def list_url(self): return self.resolve_url('list') @property def create_url(self): return self.resolve_url('create') @property def detail_url(self): return self.resolve_url('detail', args=(self.object.id,)) @property def update_url(self): return self.resolve_url('update', args=(self.object.id,)) @property def delete_url(self): return self.resolve_url('delete', args=(self.object.id,)) def get_template_names(self): names = super(BaseCrudMixin, self).get_template_names() names.append("crud/%s.html" % self.template_name_suffix.lstrip('_')) return names @property def verbose_name(self): return self.model._meta.verbose_name @property def verbose_name_plural(self): return self.model._meta.verbose_name_plural class CrudListMixin(): paginate_by = 10 no_entries_msg = _('Nenhum registro encontrado.') @property def title(self): return self.verbose_name_plural def _as_row(self, obj): return [ (get_field_display(obj, name)[1], obj.pk if i == 0 else None) for i, name in enumerate(self.list_field_names)] def get_context_data(self, **kwargs): context = super(CrudListMixin, self).get_context_data(**kwargs) # pagination page_obj = context['page_obj'] paginator = context['paginator'] context['page_range'] = make_pagination( page_obj.number, paginator.num_pages) # rows object_list = context['object_list'] context['rows'] = [self._as_row(obj) for obj in object_list] context['headers'] = [ self.model._meta.get_field(fieldname).verbose_name for fieldname in self.list_field_names] context['NO_ENTRIES_MSG'] = self.no_entries_msg return context def make_form_invalid_message(msg): return '%s %s' % (_('Formulário inválido.'), msg) class CrudCreateMixin(FormMessagesMixin): form_valid_message = _('Registro criado com sucesso!') form_invalid_message = make_form_invalid_message( _('O registro não foi criado.')) @property def title(self): return _('Adicionar %(verbose_name)s') % { 'verbose_name': self.verbose_name} @property def cancel_url(self): return self.list_url def get_success_url(self): return self.detail_url class CrudDetailMixin(): @property def title(self): # TODO: can we just use view.object in the templates???? return str(self.object) class CrudUpdateMixin(FormMessagesMixin): form_valid_message = _('Registro alterado com sucesso!') form_invalid_message = make_form_invalid_message( _('Suas alterações não foram salvas.')) @property def title(self): return str(self.object) @property def cancel_url(self): return self.detail_url def get_success_url(self): return self.detail_url class CrudDeleteMixin(FormMessagesMixin): form_valid_message = _('Registro excluído com sucesso!') form_invalid_message = make_form_invalid_message( _('O registro não foi excluído.')) def get_success_url(self): return self.list_url class Crud(object): def __init__(self, model, help_path, base_mixin=BaseCrudMixin, list_mixin=CrudListMixin, create_mixin=CrudCreateMixin, detail_mixin=CrudDetailMixin, update_mixin=CrudUpdateMixin, delete_mixin=CrudDeleteMixin): class CrudMixin(base_mixin): pass CrudMixin.model = model CrudMixin.help_path = help_path class CrudListView(CrudMixin, list_mixin, ListView): pass class CrudCreateView(CrudMixin, create_mixin, CreateView): pass class CrudDetailView(CrudMixin, detail_mixin, DetailView): pass class CrudUpdateView(CrudMixin, update_mixin, UpdateView): pass class CrudDeleteView(CrudMixin, delete_mixin, DeleteView): pass self.urlpatterns = [ url(r'^$', CrudListView.as_view(), name='list'), url(r'^create$', CrudCreateView.as_view(), name='create'), url(r'^(?P\d+)$', CrudDetailView.as_view(), name='detail'), url(r'^(?P\d+)/edit$', CrudUpdateView.as_view(), name='update'), url(r'^(?P\d+)/delete$', CrudDeleteView.as_view(), name='delete'), ] self.namespace = CrudMixin().namespace self.urls = self.urlpatterns, self.namespace, self.namespace # FIXME Refatorar código que precisa desse atributo e remover self.CrudDetailView = CrudDetailView