add decorators 'webpage' decorator for 'website', rewrite views
This commit is contained in:
@@ -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__"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
29
website/decorators.py
Normal file
29
website/decorators.py
Normal 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
153
website/mixins.py
Normal 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
|
||||
|
||||
160
website/views.py
160
website/views.py
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user