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

View File

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

View File

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