Browse Source

Add general table implementation to crud list

pull/6/head
Marcio Mazza 10 years ago
parent
commit
06d7442553
  1. 33
      sapl/crud.py
  2. 58
      sapl/test_crud.py
  3. 0
      sapl/teststubs/__init__.py
  4. 22
      sapl/teststubs/urls_for_list_test.py
  5. 120
      templates/crud/list.html

33
sapl/crud.py

@ -3,11 +3,15 @@ from crispy_forms.helper import FormHelper
from django import forms from django import forms
from django.conf.urls import url from django.conf.urls import url
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import ( from django.views.generic import (
CreateView, DeleteView, ListView, UpdateView, DetailView) CreateView, DeleteView, ListView, UpdateView, DetailView)
from sapl.layout import SaplFormLayout from sapl.layout import SaplFormLayout
from django.utils.functional import cached_property
NO_ENTRIES_MSG = _('Não existem registros')
def from_to(start, end): def from_to(start, end):
@ -114,8 +118,10 @@ def build_crud(model, *layout):
class CrudListView(BaseMixin, ListView): class CrudListView(BaseMixin, ListView):
title = BaseMixin.verbose_name_plural title = BaseMixin.verbose_name_plural
paginate_by = 10 paginate_by = 10
no_entries_msg = NO_ENTRIES_MSG
def get_fieldnames(self): @cached_property
def field_names(self):
'''The list of field names to display on table '''The list of field names to display on table
This base implementation returns the field names This base implementation returns the field names
@ -124,13 +130,24 @@ def build_crud(model, *layout):
rows = layout[0][1:] rows = layout[0][1:]
return [fieldname for row in rows for fieldname, __ in row] return [fieldname for row in rows for fieldname, __ in row]
def get_field_values(self, object_list):
return [[(get_field_display(obj, name)[1],
obj.pk if i == 0 else None)
for i, name in enumerate(self.field_names)]
for obj in object_list
]
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context_data = super(CrudListView, self).get_context_data(**kwargs) context = super(CrudListView, self).get_context_data(**kwargs)
paginator = context_data['paginator'] paginator = context['paginator']
current_page = context_data['page_obj'] page_obj = context['page_obj']
context_data['custom_page_range'] = make_pagination( context['page_range'] = make_pagination(
current_page.number, paginator.num_pages) page_obj.number, paginator.num_pages)
return context_data object_list = context['object_list']
context['field_names'] = self.field_names
context['field_values'] = self.get_field_values(object_list)
context['NO_ENTRIES_MSG'] = NO_ENTRIES_MSG
return context
class CrudCreateView(BaseMixin, FormMessagesMixin, CreateView): class CrudCreateView(BaseMixin, FormMessagesMixin, CreateView):
form_class = crud.model_form form_class = crud.model_form

58
sapl/test_crud.py

@ -3,7 +3,9 @@ from django.core.urlresolvers import reverse
from model_mommy import mommy from model_mommy import mommy
from comissoes.models import Comissao, TipoComissao from comissoes.models import Comissao, TipoComissao
from .crud import get_field_display, build_crud, make_pagination, from_to from .crud import (get_field_display, build_crud, make_pagination, from_to,
NO_ENTRIES_MSG)
from comissoes.views import comissao_crud
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@ -24,7 +26,7 @@ pytestmark = pytest.mark.django_db
def test_listview_get_fieldnames(layout, result): def test_listview_get_fieldnames(layout, result):
crud = build_crud(Comissao, *layout) crud = build_crud(Comissao, *layout)
view = crud.CrudListView() view = crud.CrudListView()
assert view.get_fieldnames() == result assert view.field_names == result
__ = None # for test readability __ = None # for test readability
@ -156,10 +158,13 @@ def assert_h1(res, title):
assert res.html.find('h1').text == title assert res.html.find('h1').text == title
NO_ENTRIES_MSG = str(NO_ENTRIES_MSG) # "unlazy"
def assert_on_list_page(res): def assert_on_list_page(res):
assert_h1(res, 'Comissões') assert_h1(res, 'Comissões')
assert 'Adicionar Comissão' in res assert 'Adicionar Comissão' in res
assert res.html.find('table') assert res.html.find('table') or NO_ENTRIES_MSG in res
# XXX ... characterize better # XXX ... characterize better
@ -177,6 +182,53 @@ def assert_on_detail_page(res, stub_name):
assert 'Excluir Comissão' in res assert 'Excluir Comissão' in res
@pytest.mark.urls('sapl.teststubs.urls_for_list_test')
@pytest.mark.parametrize("num_entries, page_size, ranges, page_list", [
(0, 6, [], []),
(5, 5, [], []),
(10, 5, [(0, 5), (5, 10)], ['«', '1', '2', '»']),
(9, 4, [(0, 4), (4, 8), (8, 9)], ['«', '1', '2', '3', '»']),
])
def test_flux_list_paginate_detail(
app, monkeypatch, num_entries, page_size, ranges, page_list):
entries_labels = []
for i in range(num_entries):
nome, sigla, tipo = 'nome %s' % i, 'sigla %s' % i, 'tipo %s' % i,
entries_labels.append([nome, sigla, tipo])
mommy.make(Comissao, nome=nome, sigla=sigla, tipo__nome=tipo)
from .teststubs.urls_for_list_test import crud
crud.CrudListView.paginate_by = page_size
res = app.get('/comissoes/')
assert_on_list_page(res)
table = res.html.find('table')
paginator = res.html.find('ul', {'class': 'pagination'})
if num_entries == 0:
assert NO_ENTRIES_MSG in res
assert not table
assert not paginator
else:
def rows():
header, *rows = table.findAll('tr')
assert header.text.strip().split() == ['nome', 'sigla', 'tipo']
return [[td.text.strip() for td in tr.findAll('td')]
for tr in rows]
if page_list:
assert paginator
assert paginator.text.strip().split() == page_list
assert entries_labels[:page_size] == rows()
# TODO... go to the second page and back
# TODO... go to detail page
@pytest.mark.parametrize("cancel, make_invalid_submit", [ @pytest.mark.parametrize("cancel, make_invalid_submit", [
(a, b) for a in (True, False) for b in (True, False)]) (a, b) for a in (True, False) for b in (True, False)])
def test_flux_list_create_detail(app, cancel, make_invalid_submit): def test_flux_list_create_detail(app, cancel, make_invalid_submit):

0
sapl/teststubs/__init__.py

22
sapl/teststubs/urls_for_list_test.py

@ -0,0 +1,22 @@
from django.conf.urls import include, url
from sapl.crud import build_crud
from comissoes.models import Comissao
crud = build_crud(
Comissao,
['Dados Básicos',
[('nome', 9), ('sigla', 3)],
[('tipo', 3)]
],
['Dados Complementares',
[('finalidade', 12)]
],
)
urlpatterns = [
url(r'^comissoes/', include(crud.urls)),
]

120
templates/crud/list.html

@ -3,66 +3,76 @@
{% block base_content %} {% block base_content %}
{# FIXME is this the best markup to use? #} {# FIXME is this the best markup to use? #}
<dl class="sub-nav"> <dl class="sub-nav">
<dd><a href="{{ view.create_url }}"> <dd><a href="{{ view.create_url }}">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %}
</a></dd> </a></dd>
</dl> </dl>
{% if not field_values %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th>{% trans 'Id' %}</th> {% for name in field_names %}
<th>{% trans 'Nome' %}</th> <th>{{ name }}</th>
<th>{% trans 'Sigla' %}</th> {% endfor %}
<th>{% trans 'Tipo' %}</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> {% for value_list in field_values %}
{% for comissao in object_list %} <tr>
<td>{{ comissao.id }}</td> {% for value, href in value_list %}
<td>{{ comissao.nome }}</td> <td>
<td>{{ comissao.sigla }}</td> {% if href %}
<td>{{ comissao.tipo }}</td> <a href="{{ href }}">{{ value }}</a>
</tr>
{% endfor %}
</tbody>
</table>
{% if is_paginated %}
<div class="pagination-centered">
<ul class="pagination" role="menubar" aria-label="Pagination">
{% if page_obj.has_previous %}
<li>
<a href="?page={{ page_obj.previous_page_number }}">
<span class="arrow">&laquo;</span>
</a>
</li>
{% else %}
<li class="arrow unavailable" aria-disabled="true"><a href="">&laquo;</a></li>
{% endif %}
{% for page in custom_page_range %}
{% if page %}
<li {% if page == page_obj.number %}class="current"{% endif %}>
<a href="?page={{ page }}">{{ page }}</a>
</li>
{% else %} {% else %}
<li class="unavailable" aria-disabled="true"><a href="">&hellip;</a></li> {{ value }}
{% endif %} {% endif %}
</td>
{% endfor %} {% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if is_paginated %}
<div class="pagination-centered">
<ul class="pagination" role="menubar" aria-label="Pagination">
{% if page_obj.has_previous %}
<li>
<a href="?page={{ page_obj.previous_page_number }}">
<span class="arrow">&laquo;</span>
</a>
</li>
{% else %}
<li class="arrow unavailable" aria-disabled="true"><a href="">&laquo;</a></li>
{% endif %}
{% for page in page_range %}
{% if page %}
<li {% if page == page_obj.number %}class="current"{% endif %}>
<a href="?page={{ page }}">{{ page }}</a>
</li>
{% else %}
<li class="unavailable" aria-disabled="true"><a href="">&hellip;</a></li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li> <li>
<a href="?page={{ page_obj.next_page_number }}"> <a href="?page={{ page_obj.next_page_number }}">
<span class="arrow">&raquo;</span> <span class="arrow">&raquo;</span>
</a> </a>
</li> </li>
{% else %} {% else %}
<li class="arrow unavailable" aria-disabled="true"><a href="">&raquo;</a></li> <li class="arrow unavailable" aria-disabled="true"><a href="">&raquo;</a></li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

Loading…
Cancel
Save