add decorators 'webpage' decorator for 'website', rewrite views

This commit is contained in:
Arthur Gerbaud
2016-08-07 16:22:19 +02:00
parent a5af70becb
commit d718984a59
7 changed files with 219 additions and 212 deletions

View File

@@ -6,7 +6,7 @@ from django.contrib import messages
from django.shortcuts import render, redirect
# Views
from django.views import generic
from website import views
# Models
from .models import ( Maraude, Maraudeur,
Rencontre, Lieu,
@@ -22,13 +22,9 @@ from .forms import ( RencontreForm, RencontreInlineFormSet,
ObservationInlineFormSet, ObservationInlineFormSetNoExtra,
MaraudeAutoDateForm, MonthSelectForm, )
from website import decorators as website
webpage = website.webpage(ajax=False, permissions=['maraudes.view_maraudes'])
class MaraudesView(views.WebsiteProtectedMixin):
class PageInfo:
title = "Maraudes ALSA"
permissions = ['maraudes.view_maraudes']
class DerniereMaraudeMixin(object):
@@ -47,7 +43,10 @@ class DerniereMaraudeMixin(object):
context['dernieres_maraudes'] = self.dernieres_maraudes
return context
class IndexView(MaraudesView, DerniereMaraudeMixin, generic.TemplateView):
@webpage
class IndexView(DerniereMaraudeMixin, generic.TemplateView):
class PageInfo:
title = "Maraude - Tableau de bord"
@@ -58,8 +57,8 @@ class IndexView(MaraudesView, DerniereMaraudeMixin, generic.TemplateView):
## MARAUDES
class MaraudeDetailsView(MaraudesView, DerniereMaraudeMixin, generic.DetailView):
@webpage
class MaraudeDetailsView(DerniereMaraudeMixin, generic.DetailView):
model = Maraude
context_object_name = "maraude"
template_name = "maraudes/details.html"
@@ -80,7 +79,8 @@ class MaraudeDetailsView(MaraudesView, DerniereMaraudeMixin, generic.DetailView)
class MaraudeListView(MaraudesView, generic.ListView):
@webpage
class MaraudeListView(generic.ListView):
model = Maraude
template_name = "maraudes/list.html"
paginate_by = 10
@@ -97,8 +97,8 @@ class MaraudeListView(MaraudesView, generic.ListView):
## COMPTE-RENDU DE MARAUDE
class CompteRenduCreateView(MaraudesView, generic.DetailView):
@webpage
class CompteRenduCreateView(generic.DetailView):
model = Maraude
template_name = "compte_rendu/compterendu_create.html"
context_object_name = "maraude"
@@ -186,7 +186,8 @@ class CompteRenduCreateView(MaraudesView, generic.DetailView):
class CompteRenduUpdateView(MaraudesView, generic.DetailView):
@webpage
class CompteRenduUpdateView(generic.DetailView):
""" Mettre à jour le compte-rendu de la maraude """
model = Maraude
context_object_name = "maraude"
@@ -253,8 +254,8 @@ class CompteRenduUpdateView(MaraudesView, generic.DetailView):
## PLANNING
class PlanningView(MaraudesView, generic.TemplateView):
@webpage
class PlanningView(generic.TemplateView):
""" Display and edit the planning of next Maraudes """
template_name = "planning/planning.html"
@@ -323,7 +324,8 @@ class PlanningView(MaraudesView, generic.TemplateView):
## LIEU
class LieuCreateView(views.WebsiteProtectedWithAjaxMixin, generic.edit.CreateView):
@website.webpage(ajax=True, permissions=['maraudes.add_lieu'])
class LieuCreateView(generic.edit.CreateView):
model = Lieu
template_name = "maraudes/lieu_create.html"
fields = "__all__"

View File

@@ -1,23 +1,18 @@
from django.shortcuts import render
from django.views import generic
from website import views
from website import decorators as website
from sujets.models import Sujet
# Create your views here.
webpage = website.webpage(ajax=False, permissions=['sujets.view_sujets'])
class SuivisView(views.WebsiteProtectedMixin):
class PageInfo:
title = "Suivi des bénéficiaires"
permissions = ['sujets.view_sujets']
class IndexView(SuivisView, generic.TemplateView):
@webpage
class IndexView(generic.TemplateView):
template_name = "suivi/index.html"
class PageInfo:
@@ -25,8 +20,8 @@ class IndexView(SuivisView, generic.TemplateView):
header = "Suivi"
header_small = "Tableau de bord"
class SuiviSujetView(SuivisView, generic.DetailView):
@webpage
class SuiviSujetView(generic.DetailView):
model = Sujet
template_name = "suivi/details.html"
context_object_name = "sujet"

View File

@@ -1,25 +1,20 @@
from django.shortcuts import render
from django.views import generic
from website import views
from website import decorators as website
from .models import Sujet
from django.forms import ModelForm
webpage = website.webpage(ajax=True, permissions=['sujets.view_sujets'])
# Create your views here.
class SujetsView(views.WebsiteProtectedMixin):
# TODO: deal with setting an active_app name other than module name
class PageInfo:
title = "Sujets"
def get_active_app(self):
return super(views.WebsiteProtectedMixin, self).get_active_app(app_name='suivi')
class SujetDetailsView(SujetsView, generic.DetailView):
@webpage
class SujetDetailsView(generic.DetailView):
template_name = "sujets/sujet_details.html"
model = Sujet
@@ -28,7 +23,8 @@ class SujetDetailsView(SujetsView, generic.DetailView):
header = "{{ sujet }}"
header_small = "suivi"
class SujetListView(SujetsView, generic.ListView):
@webpage
class SujetListView(generic.ListView):
model = Sujet
template_name = "sujets/sujet_liste.html"
@@ -36,8 +32,8 @@ class SujetListView(SujetsView, generic.ListView):
title = "Sujet - Liste des sujets"
header = "Liste des sujets"
class SujetUpdateView(SujetsView, generic.edit.UpdateView):
@webpage
class SujetUpdateView(generic.edit.UpdateView):
template_name = "sujets/sujet_update.html"
model = Sujet
fields = '__all__'
@@ -55,8 +51,8 @@ class SujetCreateForm(ModelForm):
fields = ['nom', 'surnom', 'prenom', 'genre', 'premiere_rencontre']
class SujetCreateView(views.WebsiteProtectedWithAjaxMixin, generic.edit.CreateView):
@website.webpage(ajax=True, permissions=['sujets.add_sujet'])
class SujetCreateView(generic.edit.CreateView):
template_name = "sujets/sujet_create.html"
form_class = SujetCreateForm
@@ -64,8 +60,6 @@ class SujetCreateView(views.WebsiteProtectedWithAjaxMixin, generic.edit.CreateVi
title = "Nouveau sujet"
header = "Nouveau sujet"
permissions = ['sujets.view_sujets', 'sujets.add_sujet']
def post(self, request, *args, **kwargs):
if 'next' in self.request.POST:
self.success_url = self.request.POST["next"]
@@ -78,6 +72,3 @@ class SujetCreateView(views.WebsiteProtectedWithAjaxMixin, generic.edit.CreateVi
except:
context['next'] = None
return context
#Hack
get_active_app = SujetsView.get_active_app

View File

@@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

29
website/decorators.py Normal file
View File

@@ -0,0 +1,29 @@
from .mixins import *
def _insert_bases(cls, bases):
old_bases = cls.__bases__
new_bases = tuple(bases) + old_bases
print(new_bases)
cls.__bases__ = new_bases
def webpage(**options):
try: ajax = options.pop('ajax')
except KeyError: ajax = False
try: permissions = options.pop('permissions')
except KeyError: permissions = []
new_bases = []
if ajax:
new_bases.append(WebsiteAjaxTemplateMixin)
else:
new_bases.append(WebsiteTemplateMixin)
if permissions:
new_bases.append(PermissionRequiredMixin)
def update_class(cls):
_insert_bases(cls, new_bases)
if permissions:
cls.permissions = permissions
return cls
return update_class

153
website/mixins.py Normal file
View File

@@ -0,0 +1,153 @@
import datetime
from django.utils import timezone
from django.core.exceptions import ImproperlyConfigured
from django.apps import apps
from django.contrib.auth.decorators import login_required, permission_required
from django.template import Template, Context
from django.views.generic.base import ContextMixin, TemplateResponseMixin
## Utils ##
def get_apps(app_names):
_apps = []
for name in app_names:
_apps.append(
apps.get_app_config(name)
)
return _apps
## Mixins ##
class PermissionRequiredMixin(object):
permissions = []
@classmethod
def as_view(cls, **initkwargs):
view = super(PermissionRequiredMixin, cls).as_view(**initkwargs)
return permission_required(cls.permissions)(view)
class TemplateFieldsMetaclass(type):
""" Loads Template objects with given string for
header, header_small, title, ...
Theses strings shall be found in cls.Template
"""
def __init__(cls, bases, Dict):
pass
class WebsiteTemplateMixin(TemplateResponseMixin):
""" Mixin for easy integration of 'website' templates
Each child can specify:
- title : title of the page
- header : header of the page
- header_small : sub-header of the page
If 'content_template' is not defined, value will fallback to template_name
in child view.
"""
base_template = "base_site.html"
content_template = None
class Configuration:
stylesheets = ['base.css']
navbar_apps = ['maraudes', 'suivi']
apps = get_apps(navbar_apps)
page_blocks = ['header', 'header_small', 'title']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._page_blocks = []
if not hasattr(self, "PageInfo"):
raise ImproperlyConfigured("You must define a PageInfo on ", self)
for attr, val in self.PageInfo.__dict__.items():
if attr[0] is not "_" and type(val) is str:
setattr(self, attr, Template(val))
self._page_blocks.append(attr)
def get_template_names(self):
""" Ensure same template for all children views. """
return [self.base_template]
def get_content_template(self):
if hasattr(self, 'template_name'): #Ensure easy integration with other views
self.content_template = self.template_name
return self.content_template
def get_active_app(self, app_name=None):
if not app_name:
app_name = self.__class__.__module__.split(".")[0]
return apps.get_app_config(app_name)
def get_panels(self):
""" Panneaux """
return None
def get_prochaine_maraude_for_user(self):
""" Retourne le prochain objet Maraude auquel
l'utilisateur participe, ou None """
maraudeur_cls = apps.get_model('utilisateurs', model_name="Maraudeur")
maraude_cls = apps.get_model('maraudes', model_name="Maraude")
try: #TODO: Clean up this ugly thing
self.maraudeur = maraudeur_cls.objects.get(username=self.request.user.username)
except:
self.maraudeur = None
if self.maraudeur:
return maraude_cls.objects.get_next_of(self.maraudeur)
return None
def get_prochaine_maraude(self):
return apps.get_model('maraudes', model_name="Maraude").objects.next
def _update_context_with_rendered_blocks(self, context):
""" Render text for existing PageInfo attributes.
See Configuration.page_blocks for valid attribute names """
render_context = Context(context)
for attr in self._page_blocks:
name = "page_%s" % attr
context[name] = getattr(self, attr).render(render_context)
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['stylesheets'] = self.Configuration.stylesheets
context['apps'] = self.Configuration.apps
context['active_app'] = self.get_active_app()
context['content_template'] = self.get_content_template()
context['panels'] = self.get_panels()
context['prochaine_maraude_abs'] = self.get_prochaine_maraude()
context['prochaine_maraude'] = self.get_prochaine_maraude_for_user()
self._update_context_with_rendered_blocks(context)
return context
class WebsiteAjaxTemplateMixin(WebsiteTemplateMixin):
""" Mixin that returns content_template instead of base_template when
request is Ajax.
"""
is_ajax = False
def dispatch(self, request, *args, **kwargs):
if not hasattr(self, 'content_template'):
self.content_template = self.get_content_template()
if request.is_ajax():
self.is_ajax = True
return super().dispatch(request, *args, **kwargs)
def get_template_names(self):
if self.is_ajax:
return [self.content_template]
return super().get_template_names()
class WebsiteProtectedMixin(WebsiteTemplateMixin, PermissionRequiredMixin):
pass
class WebsiteProtectedWithAjaxMixin(WebsiteAjaxTemplateMixin, PermissionRequiredMixin):
pass

View File

@@ -1,163 +1,3 @@
import datetime
from django.utils import timezone
from django.views.generic.base import ContextMixin, TemplateResponseMixin
from django.core.exceptions import ImproperlyConfigured
from django.apps import apps
from django.contrib.auth.decorators import login_required, permission_required
from django.template import Template, Context
## Utils ##
def get_apps(app_names):
_apps = []
for name in app_names:
_apps.append(
apps.get_app_config(name)
)
return _apps
## Mixins ##
class PermissionRequiredMixin(object):
permissions = []
@classmethod
def as_view(cls, **initkwargs):
view = super(PermissionRequiredMixin, cls).as_view(**initkwargs)
return permission_required(cls.permissions)(view)
class TemplateFieldsMetaclass(type):
""" Loads Template objects with given string for
header, header_small, title, ...
Theses strings shall be found in cls.Template
"""
def __init__(cls, bases, Dict):
pass
class WebsiteTemplateMixin(TemplateResponseMixin):
""" Mixin for easy integration of 'website' templates
Each child can specify:
- title : title of the page
- header : header of the page
- header_small : sub-header of the page
If 'content_template' is not defined, value will fallback to template_name
in child view.
"""
content_template = None
class Configuration:
stylesheets = ['base.css']
navbar_apps = ['maraudes', 'suivi']
apps = get_apps(navbar_apps)
page_blocks = ['header', 'header_small', 'title']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._page_blocks = []
if not hasattr(self, "PageInfo"):
raise ImproperlyConfigured("You must define a PageInfo on ", self)
for attr, val in self.PageInfo.__dict__.items():
if attr[0] is not "_" and type(val) is str:
setattr(self, attr, Template(val))
self._page_blocks.append(attr)
def get_template_names(self):
""" Ensure same template for all children views. """
return ["base_site.html"]
def get_content_template(self):
if hasattr(self, 'template_name'): #Ensure easy integration with other views
self.content_template = self.template_name
return self.content_template
def get_active_app(self, app_name=None):
if not app_name:
app_name = self.__class__.__module__.split(".")[0]
return apps.get_app_config(app_name)
def get_panels(self):
""" Panneaux """
return None
def get_prochaine_maraude_for_user(self):
""" Retourne le prochain objet Maraude auquel
l'utilisateur participe, ou None """
maraudeur_cls = apps.get_model('utilisateurs', model_name="Maraudeur")
maraude_cls = apps.get_model('maraudes', model_name="Maraude")
try: #TODO: Clean up this ugly thing
self.maraudeur = maraudeur_cls.objects.get(username=self.request.user.username)
except:
self.maraudeur = None
if self.maraudeur:
return maraude_cls.objects.get_next_of(self.maraudeur)
return None
def get_prochaine_maraude(self):
return apps.get_model('maraudes', model_name="Maraude").objects.next
def _update_context_with_rendered_blocks(self, context):
""" Render text for existing PageInfo attributes.
See Configuration.page_blocks for valid attribute names """
render_context = Context(context)
for attr in self._page_blocks:
name = "page_%s" % attr
context[name] = getattr(self, attr).render(render_context)
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['stylesheets'] = self.Configuration.stylesheets
context['apps'] = self.Configuration.apps
context['active_app'] = self.get_active_app()
context['content_template'] = self.get_content_template()
context['panels'] = self.get_panels()
context['prochaine_maraude_abs'] = self.get_prochaine_maraude()
context['prochaine_maraude'] = self.get_prochaine_maraude_for_user()
self._update_context_with_rendered_blocks(context)
return context
class WebsiteProtectedMixin(WebsiteTemplateMixin, PermissionRequiredMixin):
pass
class WebsiteProtectedWithAjaxMixin(WebsiteProtectedMixin):
""" Mixin that enables the use of 'ajax_template_name' custom template
when request is Ajax.
"""
is_ajax = False
def dispatch(self, request, *args, **kwargs):
if not hasattr(self, 'ajax_template_name'):
self.ajax_template_name = "%s_inner.html" % self.template_name.split(".")[0]
print('%s :' % self, self.ajax_template_name)
if request.is_ajax():
self.is_ajax = True
self.template_name = self.ajax_template_name
return super().dispatch(request, *args, **kwargs)
def get_template_names(self):
if self.is_ajax:
return [self.template_name]
else:
return super().get_template_names()
## Views : Index ##
from django.shortcuts import redirect
def index_view(request):