Sistema de Apoio ao Processo Legislativo
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1439 lines
52 KiB

from braces.views import FormMessagesMixin
from compressor.utils.decorators import cached_property
from crispy_forms.bootstrap import FieldWithButtons, StrictButton
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Field, Layout
from django import forms
from django.conf.urls import url
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.fields.related import ForeignKey
from django.http.response import Http404
from django.shortcuts import redirect
from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView)
from django.views.generic.base import ContextMixin
from django.views.generic.list import MultipleObjectMixin
from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
RP_LIST)
from sapl.settings import BASE_DIR
from sapl.utils import normalize
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
'list', 'create', 'detail', 'update', 'delete'
def _form_invalid_message(msg):
return '%s %s' % (_('Formulário inválido.'), msg)
FORM_MESSAGES = {ACTION_CREATE: (_('Registro criado com sucesso!'),
_('O registro não foi criado.')),
ACTION_UPDATE: (_('Registro alterado com sucesso!'),
_('Suas alterações não foram salvas.')),
ACTION_DELETE: (_('Registro excluído com sucesso!'),
_('O registro não foi excluído.'))}
FORM_MESSAGES = {k: (a, _form_invalid_message(b))
for k, (a, b) in FORM_MESSAGES.items()}
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
"""
variáveis do crud:
help_topic
container_field
container_field_set
is_m2m
model
model_set
form_search_class -> depende de o model relativo implementar SearchMixin
list_field_names
list_field_names_set -> lista reversa em details
permission_required -> este atributo ser vazio não nulo torna a view publ
layout_key_set
layout_key
ordered_list = False desativa os clicks e controles de ord da listagem
parent_field = parentesco reverso separado por '__'
namespace
return_parent_field_url
"""
class SearchMixin(models.Model):
search = models.TextField(blank=True, default='')
class Meta:
abstract = True
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None, auto_update_search=True):
if auto_update_search and hasattr(self, 'fields_search'):
search = ''
for str_field in self.fields_search:
fields = str_field.split('__')
if len(fields) == 1:
try:
search += str(getattr(self, str_field)) + ' '
except:
pass
else:
_self = self
for field in fields:
_self = getattr(_self, field)
search += str(_self) + ' '
self.search = search
self.search = normalize(self.search)
return super(SearchMixin, self).save(
force_insert=force_insert, force_update=force_update,
using=using, update_fields=update_fields)
class ListWithSearchForm(forms.Form):
q = forms.CharField(required=False, label='',
widget=forms.TextInput(
attrs={'type': 'search'}))
o = forms.CharField(required=False, label='',
widget=forms.HiddenInput())
class Meta:
fields = ['q', 'o']
def __init__(self, *args, **kwargs):
super(ListWithSearchForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.form_class = 'form-inline'
self.helper.form_method = 'GET'
self.helper.layout = Layout(
Field('o'),
FieldWithButtons(
Field('q',
placeholder=_('Filtrar Lista'),
css_class='input-lg'),
StrictButton(
_('Filtrar'), css_class='btn-default btn-lg',
type='submit'))
)
class PermissionRequiredForAppCrudMixin(PermissionRequiredMixin):
def has_permission(self):
apps = self.app_label
if isinstance(apps, str):
apps = apps,
# app_label vazio dará acesso geral
for app in apps:
if not self.request.user.has_module_perms(app):
return False
return True
class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
def has_permission(self):
perms = self.get_permission_required()
# Torna a view pública se não possuir conteudo
# no atributo permission_required
if not len(perms):
return True
for perm in perms:
if self.request.user.has_perm(perm):
return True
return False
# return self.request.user.has_perms(perms) if len(perms) else True
def dispatch(self, request, *args, **kwargs):
if not self.has_permission():
return self.handle_no_permission()
if 'pk' in kwargs:
params = {'pk': kwargs['pk']}
if self.container_field:
params[self.container_field] = request.user.pk
if not self.model.objects.filter(**params).exists():
raise Http404()
elif self.container_field:
container = self.container_field.split('__')
if len(container) > 1:
container_model = getattr(
self.model, container[0]).field.related_model
params = {}
params['__'.join(
container[1:])] = request.user.pk
if not container_model.objects.filter(**params).exists():
messages.error(
request,
'O Usuário (%s) não está registrado como (%s).' % (
request.user, container_model._meta.verbose_name))
return redirect('/')
else:
# TODO: implementar caso o user for o próprio o container
pass
return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs)
@cached_property
def container_field(self):
if hasattr(self, 'crud') and not hasattr(self.crud, 'container_field'):
self.crud.container_field = ''
if hasattr(self, 'crud'):
return self.crud.container_field
@cached_property
def container_field_set(self):
if hasattr(self, 'crud') and\
not hasattr(self.crud, 'container_field_set'):
self.crud.container_field_set = ''
if hasattr(self, 'crud'):
return self.crud.container_field_set
@cached_property
def is_contained(self):
return self.container_field_set or self.container_field
class CrudBaseMixin(CrispyLayoutFormMixin):
def __init__(self, **kwargs):
super(CrudBaseMixin, self).__init__(**kwargs)
obj = self.crud if hasattr(self, 'crud') else self
self.app_label = obj.model._meta.app_label
self.model_name = obj.model._meta.model_name
if hasattr(obj, 'model_set') and obj.model_set:
self.app_label_set = getattr(
obj.model, obj.model_set).field.model._meta.app_label
self.model_name_set = getattr(
obj.model, obj.model_set).field.model._meta.model_name
if hasattr(self, 'permission_required') and self.permission_required:
if hasattr(obj, 'public'):
self.permission_required = list(
set(self.permission_required) - set(obj.public))
else:
obj.public = []
self.permission_required = tuple((
self.permission(pr) for pr in self.permission_required))
else:
obj.public = []
@classmethod
def url_name(cls, suffix):
return '%s_%s' % (cls.model._meta.model_name, suffix)
def url_name_set(self, suffix):
obj = self.crud if hasattr(self, 'crud') else self
return '%s_%s' % (getattr(obj.model, obj.model_set
).field.model._meta.model_name, suffix)
def permission(self, rad):
return '%s%s%s' % (self.app_label if rad.endswith('_') else '',
rad,
self.model_name if rad.endswith('_') else '')
def permission_set(self, rad):
return '%s%s%s' % (self.app_label_set if rad.endswith('_') else '',
rad,
self.model_name_set if rad.endswith('_') else '')
def resolve_url(self, suffix, args=None):
namespace = self.model._meta.app_config.name
return reverse('%s:%s' % (namespace, self.url_name(suffix)),
args=args)
def resolve_url_set(self, suffix, args=None):
obj = self.crud if hasattr(self, 'crud') else self
namespace = getattr(
obj.model, obj.model_set).field.model._meta.app_config.name
return reverse('%s:%s' % (namespace, self.url_name_set(suffix)),
args=args)
@property
def ordered_list(self):
return True
@property
def list_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.ListView.permission_required:
return self.resolve_url(ACTION_LIST)
else:
return self.resolve_url(
ACTION_LIST) if self.request.user.has_perm(
self.permission(RP_LIST)) else ''
@property
def create_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.CreateView.permission_required:
return self.resolve_url(ACTION_CREATE)
else:
return self.resolve_url(
ACTION_CREATE) if self.request.user.has_perm(
self.permission(RP_ADD)) else ''
@property
def detail_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.DetailView.permission_required:
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))
else:
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))\
if self.request.user.has_perm(
self.permission(RP_DETAIL)) else ''
@property
def update_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.UpdateView.permission_required:
return self.resolve_url(ACTION_UPDATE, args=(self.object.id,))
else:
return self.resolve_url(ACTION_UPDATE, args=(self.object.id,))\
if self.request.user.has_perm(
self.permission(RP_CHANGE)) else ''
@property
def delete_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.DeleteView.permission_required:
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))
else:
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\
if self.request.user.has_perm(
self.permission(RP_DELETE)) else ''
def get_template_names(self):
names = super(CrudBaseMixin, self).get_template_names()
names.append("crud/%s.html" %
self.template_name_suffix.lstrip('_'))
return names
@property
def verbose_name_set(self):
obj = self.crud if hasattr(self, 'crud') else self
return getattr(obj.model, obj.model_set).field.model._meta.verbose_name
@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 CrudListView(PermissionRequiredContainerCrudMixin, ListView):
permission_required = (RP_LIST, )
@classmethod
def get_url_regex(cls):
return r'^$'
paginate_by = 10
no_entries_msg = _('Nenhum registro encontrado.')
def get_rows(self, object_list):
return [self._as_row(obj) for obj in object_list]
def get_headers(self):
"""
Transforma o headers de fields de list_field_names
para junção de fields via tuplas.
list_field_names pode ser construido como
list_field_names=('nome', 'endereco', ('telefone', sexo'), 'dat_nasc')
ou ainda:
list_field_names = ['composicao__comissao__nome', 'cargo__nome', (
'composicao__periodo__data_inicio', 'composicao__periodo__data_fim')]
"""
r = []
for fieldname in self.list_field_names:
if not isinstance(fieldname, tuple):
fieldname = fieldname,
s = []
for fn in fieldname:
m = self.model
fn = fn.split('__')
for f in fn:
if not f:
continue
f = m._meta.get_field(f)
if hasattr(f, 'related_model') and f.related_model:
m = f.related_model
if f:
s.append(force_text(f.verbose_name))
s = ' / '.join(s)
r.append(s)
return r
def _as_row(self, obj):
r = []
for i, name in enumerate(self.list_field_names):
url = self.resolve_url(
ACTION_DETAIL, args=(obj.id,)) if i == 0 else None
"""Caso o crud list seja para uma relação ManyToManyField"""
if url and hasattr(self, 'crud') and\
hasattr(self.crud, 'is_m2m') and self.crud.is_m2m:
url = url + ('?pkk=' + self.kwargs['pk']
if 'pk' in self.kwargs else '')
if not isinstance(name, tuple):
name = name,
""" se elemento de list_field_name for uma tupla, constrói a
informação com ' - ' se os campos forem simples,
ou com <br> se for m2m """
if isinstance(name, tuple):
s = ''
for j, n in enumerate(name):
if not n:
s += '<br>'
continue
m = obj
n = n.split('__')
for f in n[:-1]:
m = getattr(m, f)
if not m:
break
if m:
ss = get_field_display(m, n[-1])[1]
ss = (
('<br>' if '<ul>' in ss else ' - ') + ss)\
if ss and j != 0 and s else ss
s += ss
r.append((s, url))
return r
def get_context_data(self, **kwargs):
""" Relevante se na implmentação do crud list, for informado
um formulário de pesquisa herdado ou o próprio ListWithSearchForm.
Só pode ser usado se o model relativo herdar de SearchMixin"""
if hasattr(self, 'form_search_class'):
q = str(self.request.GET.get('q'))\
if 'q' in self.request.GET else ''
o = self.request.GET['o'] if 'o' in self.request.GET else '1'
if 'form' not in kwargs:
initial = self.get_initial() if hasattr(
self, 'get_initial') else {}
initial.update({'q': q, 'o': o})
kwargs['form'] = self.form_search_class(
initial=initial)
count = self.object_list.count()
context = super().get_context_data(**kwargs)
context.setdefault('title', self.verbose_name_plural)
context['count'] = count
# pagination
if self.paginate_by:
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['headers'] = self.get_headers()
context['rows'] = self.get_rows(object_list)
context['NO_ENTRIES_MSG'] = self.no_entries_msg
qr = self.request.GET.copy()
if 'page' in qr:
del qr['page']
context['filter_url'] = (
'&' + qr.urlencode()) if len(qr) > 0 else ''
if self.ordered_list:
if 'o' in qr:
del qr['o']
context['ordering_url'] = (
'&' + qr.urlencode()) if len(qr) > 0 else ''
return context
def get_queryset(self):
queryset = super().get_queryset()
# form_search_class
# só pode ser usado em models que herdam de SearchMixin
if hasattr(self, 'form_search_class'):
request = self.request
if request.GET.get('q') is not None:
query = normalize(str(request.GET.get('q')))
query = query.split(' ')
if query:
q = models.Q()
for item in query:
if not item:
continue
q = q & models.Q(search__icontains=item)
if q:
queryset = queryset.filter(q)
if self.ordered_list:
list_field_names = self.list_field_names
o = '1'
desc = ''
if 'o' in self.request.GET:
o = self.request.GET['o']
desc = '-' if o.startswith('-') else ''
# Constroi a ordenação da listagem com base no que o usuário
# clicar
try:
fields_for_ordering = list_field_names[
(abs(int(o)) - 1) % len(list_field_names)]
if isinstance(fields_for_ordering, str):
fields_for_ordering = [fields_for_ordering, ]
ordering = ()
model = self.model
for fo in fields_for_ordering:
fm = None
try:
fm = model._meta.get_field(fo)
except:
pass
if fm and hasattr(fm, 'related_model')\
and fm.related_model:
rmo = fm.related_model._meta.ordering
if rmo:
rmo = rmo[0]
if not isinstance(rmo, str):
rmo = rmo[0]
fo = '%s__%s' % (fo, rmo)
fo = desc + fo
ordering += (fo,)
model = self.model
model_ordering = model._meta.ordering
if model_ordering:
if isinstance(model_ordering, str):
model_ordering = (model_ordering,)
for mo in model_ordering:
if mo not in ordering:
ordering = ordering + (mo, )
queryset = queryset.order_by(*ordering)
# print(ordering)
except Exception as e:
print(string_concat(_(
'ERRO: construção da tupla de ordenação.'), str(e)))
# print(queryset.query)
if not self.request.user.is_authenticated():
return queryset
if self.container_field:
params = {}
params[self.container_field] = self.request.user.pk
queryset = queryset.filter(**params)
return queryset
class CrudCreateView(PermissionRequiredContainerCrudMixin,
FormMessagesMixin, CreateView):
permission_required = (RP_ADD, )
@classmethod
def get_url_regex(cls):
return r'^create$'
form_valid_message, form_invalid_message = FORM_MESSAGES[ACTION_CREATE]
@property
def cancel_url(self):
return self.list_url
def get_success_url(self):
return self.detail_url
def get_context_data(self, **kwargs):
kwargs.setdefault('title', _('Adicionar %(verbose_name)s') % {
'verbose_name': self.verbose_name})
return super(CrudCreateView, self).get_context_data(**kwargs)
def form_valid(self, form):
self.object = form.instance
try:
self.object.owner = self.request.user
self.object.modifier = self.request.user
except:
pass
if self.container_field:
container = self.container_field.split('__')
if len(container) > 1:
# TODO: implementar caso o user for próprio o container
container_model = getattr(
self.model, container[0]).field.related_model
params = {}
params['__'.join(
container[1:])] = self.request.user.pk
if 'pk' in self.kwargs:
params['pk'] = self.kwargs['pk']
container_data = container_model.objects.filter(
**params).first()
if not container_data:
raise Exception(
_('Não é permitido adicionar um registro '
'sem estar em um Container %s'
) % container_model._meta.verbose_name)
if hasattr(self, 'crud') and\
hasattr(self.crud, 'is_m2m') and self.crud.is_m2m:
setattr(
self.object, container[1], getattr(
container_data, container[1]))
response = super().form_valid(form)
getattr(self.object, container[0]).add(container_data)
return response
else:
setattr(self.object, container[0], container_data)
return super().form_valid(form)
class CrudDetailView(PermissionRequiredContainerCrudMixin,
DetailView, MultipleObjectMixin):
permission_required = (RP_DETAIL, )
no_entries_msg = _('Nenhum registro Associado.')
paginate_by = 10
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)$'
def get_rows(self, object_list):
return [self._as_row(obj) for obj in object_list]
def get_headers(self):
if not self.object_list:
return []
try:
obj = self.crud if hasattr(self, 'crud') else self
return [
(getattr(
self.object, obj.model_set).model._meta.get_field(
fieldname).verbose_name
if hasattr(self.object, fieldname) else
getattr(
self.object, obj.model_set).model._meta.get_field(
fieldname).related_model._meta.verbose_name_plural)
for fieldname in self.list_field_names_set]
except:
obj = self.crud if hasattr(self, 'crud') else self
return [getattr(
self.object,
obj.model_set).model._meta.verbose_name_plural]
def url_model_set_name(self, suffix):
return '%s_%s' % (
getattr(self.object,
self.crud.model_set).model._meta.model_name,
suffix)
def resolve_model_set_url(self, suffix, args=None):
obj = self.crud if hasattr(self, 'crud') else self
namespace = getattr(
self.object, obj.model_set).model._meta.app_config.name
return reverse('%s:%s' % (
namespace, self.url_model_set_name(suffix)),
args=args)
def _as_row(self, obj):
try:
return [(
get_field_display(obj, name)[1],
self.resolve_model_set_url(ACTION_DETAIL, args=(obj.id,))
if i == 0 else None)
for i, name in enumerate(self.list_field_names_set)]
except:
return [(
getattr(obj, name),
self.resolve_model_set_url(ACTION_DETAIL, args=(obj.id,))
if i == 0 else None)
for i, name in enumerate(self.list_field_names_set)]
def get_object(self, queryset=None):
if hasattr(self, 'object'):
return self.object
return DetailView.get_object(self, queryset=queryset)
def get(self, request, *args, **kwargs):
try:
self.object = self.model.objects.get(pk=kwargs.get('pk'))
except:
raise Http404
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set:
self.object_list = self.get_queryset()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
def get_queryset(self):
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set:
queryset = getattr(self.object, obj.model_set).all()
else:
queryset = super().get_queryset()
if not self.request.user.is_authenticated():
return queryset
if self.container_field_set:
params = {}
params[self.container_field_set] = self.request.user.pk
return queryset.filter(**params)
return queryset
def get_context_data(self, **kwargs):
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set:
count = self.object_list.count()
context = MultipleObjectMixin.get_context_data(self, **kwargs)
context['count'] = count
if self.paginate_by:
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['headers'] = self.get_headers()
context['rows'] = self.get_rows(object_list)
context['NO_ENTRIES_MSG'] = self.no_entries_msg
else:
context = ContextMixin.get_context_data(self, **kwargs)
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(
self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return context
@property
def model_set_verbose_name(self):
obj = self.crud if hasattr(self, 'crud') else self
return getattr(
self.object,
obj.model_set).model._meta.verbose_name
@property
def model_set_verbose_name_plural(self):
obj = self.crud if hasattr(self, 'crud') else self
return getattr(
self.object,
obj.model_set).model._meta.verbose_name_plural
class CrudUpdateView(PermissionRequiredContainerCrudMixin,
FormMessagesMixin, UpdateView):
permission_required = (RP_CHANGE, )
def form_valid(self, form):
self.object = form.instance
try:
self.object.modifier = self.request.user
except:
pass
return super().form_valid(form)
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)/edit$'
form_valid_message, form_invalid_message = FORM_MESSAGES[ACTION_UPDATE]
@property
def cancel_url(self):
return self.detail_url
def get_success_url(self):
return self.detail_url
class CrudDeleteView(PermissionRequiredContainerCrudMixin,
FormMessagesMixin, DeleteView):
permission_required = (RP_DELETE, )
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)/delete$'
form_valid_message, form_invalid_message = FORM_MESSAGES[ACTION_DELETE]
@property
def cancel_url(self):
return self.detail_url
def get_success_url(self):
return self.list_url
def delete(self, request, *args, **kwargs):
try:
return super(CrudDeleteView, self).delete(request, args, kwargs)
except models.ProtectedError as err:
error_msg = 'Registro não pode ser removido, pois\
é referenciado por outros registros:<br>\
<ul>'
for i in err.protected_objects:
error_msg += '<li>{} - {}</li>'.format(
i._meta.verbose_name, i
)
error_msg += '</ul>'
messages.add_message(request,
messages.ERROR,
error_msg)
return self.render_to_response(self.get_context_data())
class Crud:
BaseMixin = CrudBaseMixin
ListView = CrudListView
CreateView = CrudCreateView
DetailView = CrudDetailView
UpdateView = CrudUpdateView
DeleteView = CrudDeleteView
help_topic = ''
class PublicMixin:
permission_required = []
@classonlymethod
def get_urls(cls):
def _add_base(view):
if view:
class CrudViewWithBase(cls.BaseMixin, view):
model = cls.model
help_topic = cls.help_topic
crud = cls
CrudViewWithBase.__name__ = view.__name__
return CrudViewWithBase
CrudListView = _add_base(cls.ListView)
CrudCreateView = _add_base(cls.CreateView)
CrudDetailView = _add_base(cls.DetailView)
CrudUpdateView = _add_base(cls.UpdateView)
CrudDeleteView = _add_base(cls.DeleteView)
cruds_base = [
(CrudListView.get_url_regex()
if CrudListView else None, CrudListView, ACTION_LIST),
(CrudCreateView.get_url_regex()
if CrudCreateView else None, CrudCreateView, ACTION_CREATE),
(CrudDetailView.get_url_regex()
if CrudDetailView else None, CrudDetailView, ACTION_DETAIL),
(CrudUpdateView.get_url_regex()
if CrudUpdateView else None, CrudUpdateView, ACTION_UPDATE),
(CrudDeleteView.get_url_regex()
if CrudDeleteView else None, CrudDeleteView, ACTION_DELETE)]
cruds = []
for crud in cruds_base:
if crud[0]:
cruds.append(crud)
return [url(regex, view.as_view(), name=view.url_name(suffix))
for regex, view, suffix in cruds]
@classonlymethod
def build(cls, _model, _help_topic, _model_set=None, list_field_names=[]):
def create_class(_list_field_names):
class ModelCrud(cls):
model = _model
model_set = _model_set
help_topic = _help_topic
list_field_names = _list_field_names
return ModelCrud
ModelCrud = create_class(list_field_names)
ModelCrud.__name__ = '%sCrud' % _model.__name__
return ModelCrud
class CrudAux(Crud):
"""
Checa permissão para ver qualquer dado de tabela auxiliar
a permissão base.view_tabelas_auxiliares está definada class Meta
do model sapl.base.models.AppConfig que, naturalmente é um arquivo
de configuração geral e só pode ser acessado através das Tabelas
Auxiliares... Com isso o script de geração de perfis acaba que por
criar essa permissão apenas para o perfil Operador Geral.
"""
permission_required = ('base.view_tabelas_auxiliares',)
class ListView(Crud.ListView):
template_name = "crud/list_tabaux.html"
class BaseMixin(Crud.BaseMixin):
subnav_template_name = None
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
"""Força o template filter subnav em base/templatetags/menus.py
a abrir um yaml diferente do padrão.
Se o valor de subnav_template_name é nulo faz o filter subnav
não abrir o padrão e nem um outro arquivo.
"""
if 'subnav_template_name' not in context:
context['subnav_template_name'] = self.subnav_template_name
return context
@classonlymethod
def build(cls, _model, _help_topic, _model_set=None, list_field_names=[]):
ModelCrud = Crud.build(
_model, _help_topic, _model_set, list_field_names)
class ModelCrudAux(CrudAux, ModelCrud):
pass
return ModelCrudAux
class MasterDetailCrud(Crud):
is_m2m = False
link_return_to_parent_field = False
class BaseMixin(Crud.BaseMixin):
@property
def list_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.ListView:
return ''
return self.resolve_url(ACTION_LIST, args=(self.kwargs['pk'],))\
if self.request.user.has_perm(self.permission(RP_LIST)) else ''
@property
def create_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.CreateView:
return ''
return self.resolve_url(ACTION_CREATE, args=(self.kwargs['pk'],))\
if self.request.user.has_perm(self.permission(RP_ADD)) else ''
@property
def detail_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.DetailView:
return ''
pkk = self.request.GET['pkk'] if 'pkk' in self.request.GET else ''
return (super().detail_url + (('?pkk=' + pkk) if pkk else ''))\
if self.request.user.has_perm(
self.permission(RP_DETAIL)) else ''
@property
def update_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.UpdateView:
return ''
pkk = self.request.GET['pkk'] if 'pkk' in self.request.GET else ''
return (super().update_url + (('?pkk=' + pkk) if pkk else ''))\
if self.request.user.has_perm(
self.permission(RP_CHANGE)) else ''
@property
def delete_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.DeleteView:
return ''
return super().delete_url\
if self.request.user.has_perm(
self.permission(RP_DELETE)) else ''
def get_context_data(self, **kwargs):
obj = self.crud if hasattr(self, 'crud') else self
self.object = getattr(self, 'object', None)
parent_object = None
if self.object:
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_object = self.object
for field in fields:
parent_object = getattr(parent_object, field)
else:
parent_object = getattr(self.object, obj.parent_field)
if not isinstance(parent_object, models.Model):
if parent_object.count() > 1:
if 'pkk' not in self.request.GET:
raise Http404()
root_pk = self.request.GET['pkk']
parent_object = parent_object.filter(id=root_pk)
parent_object = parent_object.first()
if not parent_object:
raise Http404()
root_pk = parent_object.pk
else:
root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET\
else self.request.GET['pkk']
kwargs.setdefault('root_pk', root_pk)
context = super(CrudBaseMixin, self).get_context_data(**kwargs)
if parent_object:
context['title'] = '%s <small>(%s)</small>' % (
self.object, parent_object)
return context
class ListView(Crud.ListView):
permission_required = RP_LIST,
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)/%s$' % cls.model._meta.model_name
def get_context_data(self, **kwargs):
obj = self.crud if hasattr(self, 'crud') else self
context = CrudListView.get_context_data(self, **kwargs)
parent_model = None
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_model = pm = self.model
for field in fields:
pm = getattr(pm, field)
if isinstance(pm.field, ForeignKey):
parent_model = getattr(
parent_model, field).field.related_model
else:
parent_model = getattr(
parent_model, field).rel.related_model
pm = parent_model
else:
parent_model = getattr(
self.model, obj.parent_field).field.related_model
params = {'pk': kwargs['root_pk']}
if self.container_field:
container = self.container_field.split('__')
if len(container) > 1:
params['__'.join(container[1:])] = self.request.user.pk
try:
parent_object = parent_model.objects.get(**params)
except:
raise Http404()
context[
'title'] = '%s <small>(%s)</small>' % (
context['title'], parent_object)
return context
def get_queryset(self):
obj = self.crud if hasattr(self, 'crud') else self
qs = super().get_queryset()
kwargs = {obj.parent_field: self.kwargs['pk']}
"""if self.container_field:
kwargs[self.container_field] = self.request.user.pk"""
return qs.filter(**kwargs)
class CreateView(Crud.CreateView):
permission_required = RP_ADD,
def dispatch(self, request, *args, **kwargs):
return PermissionRequiredMixin.dispatch(
self, request, *args, **kwargs)
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)/%s/create$' % cls.model._meta.model_name
def get_form(self, form_class=None):
obj = self.crud if hasattr(self, 'crud') else self
form = super(MasterDetailCrud.CreateView, self).get_form(
self.form_class)
parent_field = obj.parent_field.split('__')
if not obj.is_m2m or len(parent_field) > 1:
field = self.model._meta.get_field(parent_field[0])
try:
parent = field.related_model.objects.get(
pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404()
setattr(form.instance, parent_field[0], parent)
return form
def get_context_data(self, **kwargs):
obj = self.crud if hasattr(self, 'crud') else self
context = Crud.CreateView.get_context_data(
self, **kwargs)
params = {'pk': self.kwargs['pk']}
if self.container_field:
# FIXME refatorar para parent_field com '__'
parent_model = getattr(
self.model, obj.parent_field).field.related_model
container = self.container_field.split('__')
if len(container) > 1:
params['__'.join(container[1:])] = self.request.user.pk
try:
parent_object = parent_model.objects.get(**params)
except Exception:
raise Http404()
else:
parent_model = self.model
parent_object = None
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
for field in fields:
if parent_model == self.model:
parent_model = getattr(
parent_model, field).field.related_model
parent_object = parent_model.objects.get(**params)
else:
parent_object = getattr(parent_object, field)
else:
parent_model = getattr(
parent_model, obj.parent_field).field.related_model
parent_object = parent_model.objects.get(**params)
context['root_pk'] = parent_object.pk
if parent_object:
context['title'] = '%s <small>(%s)</small>' % (
context['title'], parent_object)
return context
@property
def cancel_url(self):
if self.list_url:
return self.list_url
obj = self.crud if hasattr(self, 'crud') else self
params = {'pk': self.kwargs['pk']}
parent_model = self.model
parent_object = None
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
for field in fields:
if parent_model == self.model:
parent_model = getattr(
parent_model, field).field.related_model
parent_object = parent_model.objects.get(**params)
else:
parent_object = getattr(parent_object, field)
break
else:
parent_model = getattr(
parent_model, obj.parent_field).field.related_model
parent_object = parent_model.objects.get(**params)
return reverse(
'%s:%s' % (parent_model._meta.app_config.name,
'%s_%s' % (
parent_model._meta.model_name,
ACTION_DETAIL)),
kwargs={'pk': parent_object.pk})
class UpdateView(Crud.UpdateView):
permission_required = RP_CHANGE,
@classmethod
def get_url_regex(cls):
return r'^%s/(?P<pk>\d+)/edit$' % cls.model._meta.model_name
class DeleteView(Crud.DeleteView):
permission_required = RP_DELETE,
@classmethod
def get_url_regex(cls):
return r'^%s/(?P<pk>\d+)/delete$' % cls.model._meta.model_name
def get_success_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_object = self.object
for field in fields:
parent_object = getattr(parent_object, field)
break
else:
parent_object = getattr(self.object, obj.parent_field)
if not isinstance(parent_object, models.Model):
if parent_object.count() > 1:
if 'pkk' not in self.request.GET:
raise Http404
root_pk = self.request.GET['pkk']
parent_object = parent_object.filter(id=root_pk)
parent_object = parent_object.first()
if not parent_object:
raise Http404
root_pk = parent_object.pk
pk = root_pk
if obj.is_m2m:
namespace = parent_object._meta.app_config.name
return reverse('%s:%s' % (
namespace,
'%s_%s' % (parent_object._meta.model_name, ACTION_DETAIL)),
args=(pk,))
else:
return self.resolve_url(ACTION_LIST, args=(pk,))
class DetailView(Crud.DetailView):
permission_required = RP_DETAIL,
template_name = 'crud/detail_detail.html'
@classmethod
def get_url_regex(cls):
return r'^%s/(?P<pk>\d+)$' % cls.model._meta.model_name
@property
def detail_list_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.ListView:
return ''
if obj.ListView.permission_required not in obj.public or\
self.request.user.has_perm(self.permission(RP_LIST)):
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_object = self.object
for field in fields:
parent_object = getattr(parent_object, field)
else:
parent_object = getattr(self.object, obj.parent_field)
if not isinstance(parent_object, models.Model):
if parent_object.count() > 1:
if 'pkk' not in self.request.GET:
raise Http404
root_pk = self.request.GET['pkk']
parent_object = parent_object.filter(id=root_pk)
parent_object = parent_object.first()
if not parent_object:
raise Http404
root_pk = parent_object.pk
pk = root_pk
return self.resolve_url(ACTION_LIST, args=(pk,))
else:
return ''
@property
def detail_create_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.CreateView:
return ''
if self.request.user.has_perm(self.permission(RP_ADD)):
parent_field = obj.parent_field.split('__')[0]
parent_object = getattr(self.object, parent_field)
if not isinstance(parent_object, models.Model):
if parent_object.count() > 1:
if 'pkk' not in self.request.GET:
raise Http404
root_pk = self.request.GET['pkk']
parent_object = parent_object.filter(id=root_pk)
parent_object = parent_object.first()
if not parent_object:
raise Http404
root_pk = parent_object.pk
url = self.resolve_url(ACTION_CREATE, args=(root_pk,))
if not obj.is_m2m:
return url
else:
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_object = self.object
for field in fields:
parent_object = getattr(parent_object, field)
else:
parent_object = getattr(self.object, obj.parent_field)
return url + '?pkk=' + str(parent_object.pk)
else:
return ''
@property
def detail_set_create_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set\
and self.request.user.has_perm(
self.permission_set(RP_ADD)):
root_pk = self.object.pk
pk = root_pk
url = self.resolve_url_set(ACTION_CREATE, args=(pk,))
if not obj.is_m2m:
return url
else:
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
parent_object = self.object
for field in fields:
parent_object = getattr(parent_object, field)
else:
parent_object = getattr(self.object, obj.parent_field)
return url + '?pkk=' + str(parent_object.pk)
else:
return ''
@property
def detail_root_detail_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.link_return_to_parent_field:
return ''
if hasattr(obj, 'parent_field'):
parent_field = obj.parent_field.split('__')
if not obj.is_m2m or len(parent_field) > 1:
# field = self.model._meta.get_field(parent_field[0])
if isinstance(getattr(
self.object, parent_field[0]), models.Model):
parent_object = getattr(self.object, parent_field[0])
root_pk = parent_object.pk
pk = root_pk
namespace = parent_object._meta.app_config.name
return reverse('%s:%s' % (
namespace,
'%s_%s' % (parent_object._meta.model_name,
ACTION_DETAIL)),
args=(pk,))
return ''
@property
def detail_root_detail_verbose_name(self):
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'parent_field'):
parent_field = obj.parent_field.split('__')
if not obj.is_m2m or len(parent_field) > 1:
field = self.model._meta.get_field(parent_field[0])
return field.verbose_name
return ''
@classonlymethod
def build(cls, model, parent_field, help_topic,
_model_set=None, list_field_names=[]):
crud = super(MasterDetailCrud, cls).build(
model, help_topic, _model_set=_model_set,
list_field_names=list_field_names)
crud.parent_field = parent_field
return crud
class CrudBaseForListAndDetailExternalAppView(MasterDetailCrud):
CreateView, UpdateView, DeleteView = None, None, None
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
def resolve_url(self, suffix, args=None):
obj = self.crud if hasattr(self, 'crud') else self
""" namespace deve ser redirecionado para app local pois
o models colocados nos cruds que herdam este Crud são de outras app
"""
return reverse('%s:%s' % (obj.namespace, self.url_name(suffix)),
args=args)