From 58605ae3ffe81f3874e60c7fafe291ee8b7ce4e5 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 23:07:43 +0100 Subject: [PATCH 1/5] Optimisation chargement des variables de conf et ipv6 --- cotisations/forms.py | 9 ++- cotisations/views.py | 16 ++--- logs/views.py | 6 +- .../migrations/0076_auto_20180130_1623.py | 21 +++++++ machines/models.py | 21 +++---- machines/views.py | 5 +- preferences/models.py | 62 ++++++++++++++++++- re2o/acl.py | 1 - re2o/context_processors.py | 8 +-- re2o/utils.py | 16 +++-- re2o/views.py | 3 +- search/views.py | 3 +- topologie/views.py | 6 +- users/forms.py | 6 +- users/models.py | 30 ++++----- users/views.py | 20 +++--- 16 files changed, 142 insertions(+), 91 deletions(-) create mode 100644 machines/migrations/0076_auto_20180130_1623.py diff --git a/cotisations/forms.py b/cotisations/forms.py index c4e02397..ba4c066f 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -296,9 +296,8 @@ class RechargeForm(Form): def clean_value(self): value = self.cleaned_data['value'] - options, _created = OptionalUser.objects.get_or_create() - if value < options.min_online_payment: - raise forms.ValidationError("Montant inférieur au montant minimal de paiement en ligne (%s) €" % options.min_online_payment) - if value + self.user.solde > options.max_solde: - raise forms.ValidationError("Le solde ne peux excéder %s " % options.max_solde) + if value < OptionalUser.get_cached_value('min_online_payment'): + raise forms.ValidationError("Montant inférieur au montant minimal de paiement en ligne (%s) €" % OptionalUser.get_cached_value('min_online_payment')) + if value + self.user.solde > OptionalUser.get_cached_value('max_solde'): + raise forms.ValidationError("Le solde ne peux excéder %s " % OptionalUser.get_cached_value('max_solde')) return value diff --git a/cotisations/views.py b/cotisations/views.py index 16d25295..bc0c586e 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -106,9 +106,8 @@ def new_facture(request, user, userid): articles = article_formset # Si au moins un article est rempli if any(art.cleaned_data for art in articles): - options, _created = OptionalUser.objects.get_or_create() - user_solde = options.user_solde - solde_negatif = options.solde_negatif + user_solde = OptionalUser.get_cached_value('user_solde') + solde_negatif = OptionalUser.get_cached_value('solde_negatif') # Si on paye par solde, que l'option est activée, # on vérifie que le négatif n'est pas atteint if user_solde: @@ -498,8 +497,7 @@ def del_banque(request, instances): def control(request): """Pour le trésorier, vue pour controler en masse les factures.Case à cocher, pratique""" - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') facture_list = Facture.objects.select_related('user').select_related('paiement') facture_list = SortTable.sort( facture_list, @@ -567,8 +565,7 @@ def index_banque(request): @can_view_all(Facture) def index(request): """Affiche l'ensemble des factures, pour les cableurs et +""" - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') facture_list = Facture.objects.select_related('user')\ .select_related('paiement').prefetch_related('vente_set') facture_list = SortTable.sort( @@ -617,9 +614,8 @@ def new_facture_solde(request, userid): articles = article_formset # Si au moins un article est rempli if any(art.cleaned_data for art in articles): - options, _created = OptionalUser.objects.get_or_create() - user_solde = options.user_solde - solde_negatif = options.solde_negatif + user_solde = OptionalUser.get_cached_value('user_solde') + solde_negatif = OptionalUser.get_cached_value('solde_negatif') # Si on paye par solde, que l'option est activée, # on vérifie que le négatif n'est pas atteint if user_solde: diff --git a/logs/views.py b/logs/views.py index d899d2f6..48e52fe0 100644 --- a/logs/views.py +++ b/logs/views.py @@ -121,8 +121,7 @@ STATS_DICT = { def index(request): """Affiche les logs affinés, date reformatées, selectionne les event importants (ajout de droits, ajout de ban/whitelist)""" - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') # The types of content kept for display content_type_filter = ['ban', 'whitelist', 'vente', 'interface', 'user'] # Select only wanted versions @@ -180,8 +179,7 @@ def index(request): def stats_logs(request): """Affiche l'ensemble des logs et des modifications sur les objets, classés par date croissante, en vrac""" - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') revisions = Revision.objects.all().select_related('user')\ .prefetch_related('version_set__object') revisions = SortTable.sort( diff --git a/machines/migrations/0076_auto_20180130_1623.py b/machines/migrations/0076_auto_20180130_1623.py new file mode 100644 index 00000000..9aab328b --- /dev/null +++ b/machines/migrations/0076_auto_20180130_1623.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-30 15:23 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0075_auto_20180130_0052'), + ] + + operations = [ + migrations.AlterField( + model_name='ipv6list', + name='interface', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ipv6list', to='machines.Interface'), + ), + ] diff --git a/machines/models.py b/machines/models.py index b0e0086d..afb072a7 100644 --- a/machines/models.py +++ b/machines/models.py @@ -94,8 +94,7 @@ class Machine(FieldPermissionModelMixin, models.Model): user = users.models.User.objects.get(pk=userid) except users.models.User.DoesNotExist: return False, u"Utilisateur inexistant" - options, created = preferences.models.OptionalMachine.objects.get_or_create() - max_lambdauser_interfaces = options.max_lambdauser_interfaces + max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_interfaces') if not user_request.has_perm('machines.add_machine'): if user != user_request: return False, u"Vous ne pouvez pas ajouter une machine à un\ @@ -1244,7 +1243,6 @@ class Interface(FieldPermissionModelMixin,models.Model): def sync_ipv6(self): """Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi""" - machine_options, _created = preferences.models.OptionalMachine.objects.get_or_create() if machine_options.ipv6_mode == 'SLAAC': self.sync_ipv6_slaac() elif machine_options.ipv6_mode == 'DHCPV6': @@ -1255,11 +1253,10 @@ class Interface(FieldPermissionModelMixin,models.Model): def ipv6(self): """ Renvoie le queryset de la liste des ipv6 On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)""" - machine_options, _created = preferences.models.OptionalMachine.objects.get_or_create() - if machine_options.ipv6_mode == 'SLAAC': - return Ipv6List.objects.filter(interface=self) - elif machine_options.ipv6_mode == 'DHCPV6': - return Ipv6List.objects.filter(interface=self, slaac_ip=False) + if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC': + return self.ipv6list.all() + elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6': + return self.ipv6list.filter(slaac_ip=False) else: return None @@ -1338,8 +1335,7 @@ class Interface(FieldPermissionModelMixin,models.Model): except Machine.DoesNotExist: return False, u"Machine inexistante" if not user_request.has_perm('machines.add_interface'): - options, created = preferences.models.OptionalMachine.objects.get_or_create() - max_lambdauser_interfaces = options.max_lambdauser_interfaces + max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_interfaces') if machine.user != user_request: return False, u"Vous ne pouvez pas ajouter une interface à une\ machine d'un autre user que vous sans droit" @@ -1432,7 +1428,7 @@ class Ipv6List(FieldPermissionModelMixin, models.Model): protocol='IPv6', unique=True ) - interface = models.ForeignKey('Interface', on_delete=models.CASCADE) + interface = models.ForeignKey('Interface', on_delete=models.CASCADE, related_name='ipv6list') slaac_ip = models.BooleanField(default=False) class Meta: @@ -1655,8 +1651,7 @@ class Domain(models.Model): except Interface.DoesNotExist: return False, u"Interface inexistante" if not user_request.has_perm('machines.add_domain'): - options, created = preferences.models.OptionalMachine.objects.get_or_create() - max_lambdauser_aliases = options.max_lambdauser_aliases + max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_aliases') if interface.machine.user != user_request: return False, u"Vous ne pouvez pas ajouter un alias à une\ machine d'un autre user que vous sans droit" diff --git a/machines/views.py b/machines/views.py index 9b766779..c954766c 100644 --- a/machines/views.py +++ b/machines/views.py @@ -981,8 +981,7 @@ def del_nas(request, instances): @login_required @can_view_all(Machine) def index(request): - options, created = GeneralOption.objects.get_or_create() - pagination_large_number = options.pagination_large_number + pagination_large_number = GeneralOption.get_cached_value('pagination_large_number') machines_list = Machine.objects.select_related('user').prefetch_related('interface_set__domain__extension').prefetch_related('interface_set__ipv4__ip_type').prefetch_related('interface_set__type__ip_type__extension').prefetch_related('interface_set__domain__related_domain__extension') machines_list = SortTable.sort( machines_list, @@ -1166,7 +1165,7 @@ def mac_ip_list(request): @login_required @permission_required('machines.serveur') def full_mac_ip_list(request): - interfaces = all_active_assigned_interfaces() + interfaces = all_active_assigned_interfaces(full=True) seria = FullInterfaceSerializer(interfaces, many=True) return seria.data diff --git a/preferences/models.py b/preferences/models.py index a2837e52..54de71b1 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -31,6 +31,7 @@ import cotisations.models import machines.models from django.db.models.signals import post_save, post_delete from django.dispatch import receiver +from django.core.cache import cache from .aes_field import AESEncryptedField @@ -67,6 +68,20 @@ class OptionalUser(models.Model): help_text="Un nouvel utilisateur peut se créer son compte sur re2o" ) + @classmethod + def set_in_cache(cls, key): + machine_options, _created = cls.objects.get_or_create() + value = getattr(machine_options, key) + cache.set('optionaluser_' + key, value, None) + return value + + @classmethod + def get_cached_value(cls, key): + value = cache.get('optionaluser_' + key) + if value == None: + value = cls.set_in_cache(key) + return value + class Meta: permissions = ( ("view_optionaluser", "Peut voir les options de l'user"), @@ -158,7 +173,21 @@ class OptionalMachine(models.Model): @cached_property def ipv6(self): - return not self.ipv6_mode == 'DISABLED' + return not self.get_cached_value('ipv6_mode') == 'DISABLED' + + @classmethod + def set_in_cache(cls, key): + machine_options, _created = cls.objects.get_or_create() + value = getattr(machine_options, key) + cache.set('optionalmachine_' + key, value, None) + return value + + @classmethod + def get_cached_value(cls, key): + value = cache.get('optionalmachine_' + key) + if value == None: + value = cls.set_in_cache(key) + return value class Meta: permissions = ( @@ -220,9 +249,10 @@ class OptionalMachine(models.Model): @receiver(post_save, sender=OptionalMachine) -def interface_post_save(sender, **kwargs): +def optionalmachine_post_save(sender, **kwargs): """Synchronisation ipv6""" machine_pref = kwargs['instance'] + if machine_pref.ipv6_mode != "DISABLED": for interface in machines.models.Interface.objects.all(): interface.sync_ipv6() @@ -260,6 +290,20 @@ class OptionalTopologie(models.Model): null=True ) + @classmethod + def set_in_cache(cls, key): + machine_options, _created = cls.objects.get_or_create() + value = getattr(machine_options, key) + cache.set('optionaltopologie_' + key, value, None) + return value + + @classmethod + def get_cached_value(cls, key): + value = cache.get('optionaltopologie_' + key) + if value == None: + value = cls.set_in_cache(key) + return value + class Meta: permissions = ( ("view_optionaltopologie", "Peut voir les options de topologie"), @@ -345,6 +389,20 @@ class GeneralOption(models.Model): blank=True, ) + @classmethod + def set_in_cache(cls, key): + machine_options, _created = cls.objects.get_or_create() + value = getattr(machine_options, key) + cache.set('generaloption_' + key, value, None) + return value + + @classmethod + def get_cached_value(cls, key): + value = cache.get('generaloption_' + key) + if value == None: + value = cls.set_in_cache(key) + return value + class Meta: permissions = ( ("view_generaloption", "Peut voir les options générales"), diff --git a/re2o/acl.py b/re2o/acl.py index 23636ee4..f7fa75d0 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -45,7 +45,6 @@ def can_create(model): def decorator(view): def wrapper(request, *args, **kwargs): can, msg = model.can_create(request.user, *args, **kwargs) - #options, _created = OptionalUser.objects.get_or_create() if not can: messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") return redirect(reverse('index')) diff --git a/re2o/context_processors.py b/re2o/context_processors.py index f696c3f4..5a34e34b 100644 --- a/re2o/context_processors.py +++ b/re2o/context_processors.py @@ -31,10 +31,8 @@ from preferences.models import GeneralOption, OptionalMachine def context_user(request): """Fonction de context lorsqu'un user est logué (ou non), renvoie les infos sur l'user, la liste de ses droits, ses machines""" - general_options, _created = GeneralOption.objects.get_or_create() - machine_options, _created = OptionalMachine.objects.get_or_create() user = request.user - global_message = general_options.general_message + global_message = GeneralOption.get_cached_value('general_message') if global_message: messages.warning(request, global_message) if user.is_authenticated(): @@ -44,6 +42,6 @@ def context_user(request): return { 'request_user': user, 'interfaces': interfaces, - 'site_name': general_options.site_name, - 'ipv6_enabled': machine_options.ipv6, + 'site_name': GeneralOption.get_cached_value('site_name'), + 'ipv6_enabled': OptionalMachine.get_cached_value('ipv6'), } diff --git a/re2o/utils.py b/re2o/utils.py index 23c6e052..27feecae 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -121,15 +121,23 @@ def filter_active_interfaces(interface_set): .distinct() -def all_active_interfaces(): +def filter_complete_interfaces(interface_set): + """Appel la fonction précédente avec un prefetch_related ipv6 en plus""" + return filter_active_interfaces(interface_set).prefetch_related('ipv6list') + + +def all_active_interfaces(full=False): """Renvoie l'ensemble des machines autorisées à sortir sur internet """ - return filter_active_interfaces(Interface.objects) + if full: + return filter_complete_interfaces(Interface.objects) + else: + return filter_active_interfaces(Interface.objects) -def all_active_assigned_interfaces(): +def all_active_assigned_interfaces(full=False): """ Renvoie l'ensemble des machines qui ont une ipv4 assignées et disposant de l'accès internet""" - return all_active_interfaces().filter(ipv4__isnull=False) + return all_active_interfaces(full=full).filter(ipv4__isnull=False) def all_active_interfaces_count(): diff --git a/re2o/views.py b/re2o/views.py index cf08b08e..641e587e 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -136,8 +136,7 @@ def history(request, application, object_name, object_id): 'users:profil', kwargs={'userid':str(request.user.id)} )) - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') reversions = Version.objects.get_for_object(instance) paginator = Paginator(reversions, pagination_number) page = request.GET.get('page') diff --git a/search/views.py b/search/views.py index 5b88febd..cc33bb7c 100644 --- a/search/views.py +++ b/search/views.py @@ -111,8 +111,7 @@ def finish_results(results, col, order): SortTable.TOPOLOGIE_INDEX ) - options, _ = GeneralOption.objects.get_or_create() - max_result = options.search_display_page + max_result = GeneralOption.get_cached_value('search_display_page') for name, val in results.items(): results[name] = val.distinct()[:max_result] results.update({'max_result': max_result}) diff --git a/topologie/views.py b/topologie/views.py index b13acb20..828488c8 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -99,8 +99,7 @@ def index(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX ) - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') paginator = Paginator(switch_list, pagination_number) page = request.GET.get('page') try: @@ -153,8 +152,7 @@ def index_room(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX_ROOM ) - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') paginator = Paginator(room_list, pagination_number) page = request.GET.get('page') try: diff --git a/users/forms.py b/users/forms.py index 74d6694e..e52c79fb 100644 --- a/users/forms.py +++ b/users/forms.py @@ -289,8 +289,7 @@ class AdherentForm(FieldPermissionFormMixin, ModelForm): """Verifie que le tel est présent si 'option est validée dans preferences""" telephone = self.cleaned_data['telephone'] - preferences, _created = OptionalUser.objects.get_or_create() - if not telephone and preferences.is_tel_mandatory: + if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'): raise forms.ValidationError( "Un numéro de téléphone valide est requis" ) @@ -341,8 +340,7 @@ class ClubForm(FieldPermissionFormMixin, ModelForm): """Verifie que le tel est présent si 'option est validée dans preferences""" telephone = self.cleaned_data['telephone'] - preferences, _created = OptionalUser.objects.get_or_create() - if not telephone and preferences.is_tel_mandatory: + if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'): raise forms.ValidationError( "Un numéro de téléphone valide est requis" ) diff --git a/users/models.py b/users/models.py index d3c1d175..19fca2c3 100644 --- a/users/models.py +++ b/users/models.py @@ -406,8 +406,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): """ Renvoie le solde d'un user. Vérifie que l'option solde est activé, retourne 0 sinon. Somme les crédits de solde et retire les débit payés par solde""" - options, _created = OptionalUser.objects.get_or_create() - user_solde = options.user_solde + user_solde = OptionalUser.get_cached_value('user_solde') if user_solde: solde_objects = Paiement.objects.filter(moyen='Solde') somme_debit = Vente.objects.filter( @@ -534,7 +533,6 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): assooptions, _created = AssoOption.objects.get_or_create() mailmessageoptions, _created = MailMessageOption\ .objects.get_or_create() - general_options, _created = GeneralOption.objects.get_or_create() context = Context({ 'nom': self.get_full_name(), 'asso_name': assooptions.name, @@ -548,7 +546,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): 'name': assooptions.name }, '', - general_options.email_from, + GeneralOption.get_cached_value('email_from'), [self.email], html_message=template.render(context) ) @@ -563,21 +561,20 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): req.save() template = loader.get_template('users/email_passwd_request') options, _created = AssoOption.objects.get_or_create() - general_options, _created = GeneralOption.objects.get_or_create() context = { 'name': req.user.get_full_name(), 'asso': options.name, 'asso_mail': options.contact, - 'site_name': general_options.site_name, + 'site_name': GeneralOption.get_cached_value('site_name'), 'url': request.build_absolute_uri( reverse('users:process', kwargs={'token': req.token})), - 'expire_in': str(general_options.req_expire_hrs) + ' heures', + 'expire_in': str(GeneralOption.get_cached_value('req_expire_hrs')) + ' heures', } send_mail( 'Changement de mot de passe du %(name)s / Password\ renewal for %(name)s' % {'name': options.name}, template.render(context), - general_options.email_from, + GeneralOption.get_cached_value('email_from'), [req.user.email], fail_silently=False ) @@ -587,8 +584,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" all_interfaces = self.user_interfaces(active=False) - options, _created = OptionalMachine.objects.get_or_create() - if all_interfaces.count() > options.max_lambdauser_interfaces: + if all_interfaces.count() > OptionalMachine.get_cached_value('max_lambdauser_interfaces'): return False, "Maximum de machines enregistrees atteinte" if not nas_type: return False, "Re2o ne sait pas à quel machinetype affecter cette\ @@ -622,7 +618,6 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): ajoutée par le radius""" template = loader.get_template('users/email_auto_newmachine') assooptions, _created = AssoOption.objects.get_or_create() - general_options, _created = GeneralOption.objects.get_or_create() context = Context({ 'nom': self.get_full_name(), 'mac_address' : interface.mac_address, @@ -634,7 +629,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): send_mail( "Ajout automatique d'une machine / New machine autoregistered", '', - general_options.email_from, + GeneralOption.get_cached_value('email_from'), [self.email], html_message=template.render(context) ) @@ -683,11 +678,10 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): :return: a message and a boolean which is True if the user can create an user or if the `options.all_can_create` is set. """ - options, _created = OptionalUser.objects.get_or_create() - if(not user_request.is_authenticated and not options.self_adhesion): + if(not user_request.is_authenticated and not OptionalUser.get_cached_value('self_adhesion')): return False, None else: - if(options.all_can_create or options.self_adhesion): + if(OptionalUser.get_cached_value('all_can_create') or OptionalUser.get_cached_value('self_adhesion')): return True, None else: return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\ @@ -1265,7 +1259,6 @@ class Ban(models.Model): def notif_ban(self): """ Prend en argument un objet ban, envoie un mail de notification """ - general_options, _created = GeneralOption.objects.get_or_create() template = loader.get_template('users/email_ban_notif') options, _created = AssoOption.objects.get_or_create() context = Context({ @@ -1277,7 +1270,7 @@ class Ban(models.Model): send_mail( 'Deconnexion disciplinaire', template.render(context), - general_options.email_from, + GeneralOption.get_cached_value('email_from'), [self.user.email], fail_silently=False ) @@ -1499,9 +1492,8 @@ class Request(models.Model): def save(self): if not self.expires_at: - options, _created = GeneralOption.objects.get_or_create() self.expires_at = timezone.now() \ - + datetime.timedelta(hours=options.req_expire_hrs) + + datetime.timedelta(hours=GeneralOption.get_cached_value('req_expire_hrs')) if not self.token: self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens super(Request, self).save() diff --git a/users/views.py b/users/views.py index 40fd80d0..5981d4b1 100644 --- a/users/views.py +++ b/users/views.py @@ -122,9 +122,8 @@ def new_user(request): """ Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" user = AdherentForm(request.POST or None, user=request.user) - options, _created = GeneralOption.objects.get_or_create() - GTU_sum_up = options.GTU_sum_up - GTU = options.GTU + GTU_sum_up = GeneralOption.get_cached_value('GTU_sum_up') + GTU = GeneralOption.get_cached_value('GTU') if user.is_valid(): user = user.save(commit=False) with transaction.atomic(), reversion.create_revision(): @@ -595,8 +594,7 @@ def mass_archive(request): @can_view_all(Adherent) def index(request): """ Affiche l'ensemble des adherents, need droit cableur """ - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') users_list = Adherent.objects.select_related('room') users_list = SortTable.sort( users_list, @@ -621,8 +619,7 @@ def index(request): @can_view_all(Club) def index_clubs(request): """ Affiche l'ensemble des clubs, need droit cableur """ - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') clubs_list = Club.objects.select_related('room') clubs_list = SortTable.sort( clubs_list, @@ -647,8 +644,7 @@ def index_clubs(request): @can_view_all(Ban) def index_ban(request): """ Affiche l'ensemble des ban, need droit cableur """ - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') ban_list = Ban.objects.select_related('user') ban_list = SortTable.sort( ban_list, @@ -673,8 +669,7 @@ def index_ban(request): @can_view_all(Whitelist) def index_white(request): """ Affiche l'ensemble des whitelist, need droit cableur """ - options, _created = GeneralOption.objects.get_or_create() - pagination_number = options.pagination_number + pagination_number = GeneralOption.get_cached_value('pagination_number') white_list = Whitelist.objects.select_related('user') white_list = SortTable.sort( white_list, @@ -780,8 +775,7 @@ def profil(request, users, userid): request.GET.get('order'), SortTable.USERS_INDEX_WHITE ) - options, _created = OptionalUser.objects.get_or_create() - user_solde = options.user_solde + user_solde = OptionalUser.get_cached_value('user_solde') options, _created = AssoOption.objects.get_or_create() allow_online_payment = options.payment != 'NONE' return render( From 8015b27baaeb22e59f06b63dffafbaee9cd9337c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 31 Jan 2018 02:46:48 +0100 Subject: [PATCH 2/5] Optimisation chargement de la page gestion droits --- users/forms.py | 2 +- users/views.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/users/forms.py b/users/forms.py index e52c79fb..72094d22 100644 --- a/users/forms.py +++ b/users/forms.py @@ -441,7 +441,7 @@ class ListRightForm(ModelForm): """Edition, d'un groupe , équivalent à un droit Ne peremet pas d'editer le gid, car il sert de primary key""" permissions = forms.ModelMultipleChoiceField( - Permission.objects.all(), + Permission.objects.all().select_related('content_type'), widget=forms.CheckboxSelectMultiple, required=False ) diff --git a/users/views.py b/users/views.py index 5981d4b1..11da6843 100644 --- a/users/views.py +++ b/users/views.py @@ -710,7 +710,8 @@ def index_school(request): @can_view_all(ListRight) def index_listright(request): """ Affiche l'ensemble des droits , need droit cableur """ - listright_list = ListRight.objects.order_by('unix_name') + listright_list = ListRight.objects.order_by('unix_name')\ + .prefetch_related('permissions').prefetch_related('user_set') return render( request, 'users/index_listright.html', From a3c0a50f4ac6f8b26d258508e0ee44571e0d08f6 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 31 Jan 2018 03:27:59 +0100 Subject: [PATCH 3/5] Optimisation sur assooption et divers --- cotisations/payment.py | 10 ++++------ cotisations/views.py | 31 ++++++++++++++----------------- machines/views.py | 2 +- preferences/models.py | 13 +++++++++++++ topologie/views.py | 2 +- users/models.py | 22 +++++++++------------- users/views.py | 3 +-- 7 files changed, 43 insertions(+), 40 deletions(-) diff --git a/cotisations/payment.py b/cotisations/payment.py index 07cbe5dc..f9a66bf6 100644 --- a/cotisations/payment.py +++ b/cotisations/payment.py @@ -38,7 +38,6 @@ def refuse_payment(request): @csrf_exempt def ipn(request): - option, _created = AssoOption.objects.get_or_create() p = ComnpayPayment() order = ('idTpe', 'idTransaction', 'montant', 'result', 'sec', ) try: @@ -46,7 +45,7 @@ def ipn(request): except MultiValueDictKeyError: return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") - if not p.validSec(data, option.payment_pass): + if not p.validSec(data, AssoOption.get_cached_value('payment_pass')): return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") result = True if (request.POST['result'] == 'OK') else False @@ -54,7 +53,7 @@ def ipn(request): idTransaction = request.POST['idTransaction'] # On vérifie que le paiement nous est destiné - if not idTpe == option.payment_id: + if not idTpe == AssoOption.get_cached_value('payment_id'): return HttpResponseBadRequest("HTTP/1.1 400 Bad Request") try: @@ -81,10 +80,9 @@ def ipn(request): def comnpay(facture, request): host = request.get_host() - option, _created = AssoOption.objects.get_or_create() p = ComnpayPayment( - str(option.payment_id), - str(option.payment_pass), + str(AssoOption.get_cached_value('payment_id')), + str(AssoOption.get_cached_value('payment_pass')), 'https://' + host + reverse( 'cotisations:accept_payment', kwargs={'factureid':facture.id} diff --git a/cotisations/views.py b/cotisations/views.py index bc0c586e..e5f929b2 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -180,7 +180,6 @@ def new_facture_pdf(request): Vente ou Facture correspondant en bdd""" facture_form = NewFactureFormPdf(request.POST or None) if facture_form.is_valid(): - options, _created = AssoOption.objects.get_or_create() tbl = [] article = facture_form.cleaned_data['article'] quantite = facture_form.cleaned_data['number'] @@ -199,12 +198,12 @@ def new_facture_pdf(request): 'article': tbl, 'total': prix_total, 'paid': paid, - 'asso_name': options.name, - 'line1': options.adresse1, - 'line2': options.adresse2, - 'siret': options.siret, - 'email': options.contact, - 'phone': options.telephone, + 'asso_name': AssoOption.get_cached_value('name'), + 'line1': AssoOption.get_cached_value('adresse1'), + 'line2': AssoOption.get_cached_value('adresse2'), + 'siret': AssoOption.get_cached_value('siret'), + 'email': AssoOption.get_cached_value('contact'), + 'phone': AssoOption.get_cached_value('telephone'), 'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH) }) return form({ @@ -222,7 +221,6 @@ def facture_pdf(request, facture, factureid): ventes_objects = Vente.objects.all().filter(facture=facture) ventes = [] - options, _created = AssoOption.objects.get_or_create() for vente in ventes_objects: ventes.append([vente, vente.number, vente.prix_total]) return render_invoice(request, { @@ -232,12 +230,12 @@ def facture_pdf(request, facture, factureid): 'dest': facture.user, 'article': ventes, 'total': facture.prix_total(), - 'asso_name': options.name, - 'line1': options.adresse1, - 'line2': options.adresse2, - 'siret': options.siret, - 'email': options.contact, - 'phone': options.telephone, + 'asso_name': AssoOption.get_cached_value('name'), + 'line1': AssoOption.get_cached_value('adresse1'), + 'line2': AssoOption.get_cached_value('adresse2'), + 'siret': AssoOption.get_cached_value('siret'), + 'email': AssoOption.get_cached_value('contact'), + 'phone': AssoOption.get_cached_value('telephone'), 'tpl_path': os.path.join(settings.BASE_DIR, LOGO_PATH) }) @@ -683,8 +681,7 @@ def new_facture_solde(request, userid): @login_required def recharge(request): - options, _created = AssoOption.objects.get_or_create() - if options.payment == 'NONE': + if AssoOption.get_cached_value('payment') == 'NONE': messages.error( request, "Le paiement en ligne est désactivé." @@ -707,6 +704,6 @@ def recharge(request): number=1, ) v.save() - content = payment.PAYMENT_SYSTEM[options.payment](facture, request) + content = payment.PAYMENT_SYSTEM[AssoOption.get_cached_value('payment')](facture, request) return render(request, 'cotisations/payment.html', content) return form({'rechargeform':f}, 'cotisations/recharge.html', request) diff --git a/machines/views.py b/machines/views.py index c954766c..9208f3fb 100644 --- a/machines/views.py +++ b/machines/views.py @@ -982,7 +982,7 @@ def del_nas(request, instances): @can_view_all(Machine) def index(request): pagination_large_number = GeneralOption.get_cached_value('pagination_large_number') - machines_list = Machine.objects.select_related('user').prefetch_related('interface_set__domain__extension').prefetch_related('interface_set__ipv4__ip_type').prefetch_related('interface_set__type__ip_type__extension').prefetch_related('interface_set__domain__related_domain__extension') + machines_list = Machine.objects.select_related('user').prefetch_related('interface_set__domain__extension').prefetch_related('interface_set__ipv4__ip_type').prefetch_related('interface_set__type__ip_type__extension').prefetch_related('interface_set__domain__related_domain__extension').prefetch_related('interface_set__ipv6list') machines_list = SortTable.sort( machines_list, request.GET.get('col'), diff --git a/preferences/models.py b/preferences/models.py index 54de71b1..6bc101d1 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -571,6 +571,19 @@ class AssoOption(models.Model): blank=True, ) + @classmethod + def set_in_cache(cls, key): + machine_options, _created = cls.objects.get_or_create() + value = getattr(machine_options, key) + cache.set('assooption_' + key, value, None) + return value + + @classmethod + def get_cached_value(cls, key): + value = cache.get('assooption_' + key) + if value == None: + value = cls.set_in_cache(key) + return value class Meta: permissions = ( diff --git a/topologie/views.py b/topologie/views.py index 828488c8..cc129f42 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -190,7 +190,7 @@ def index_stack(request): @can_view_all(ConstructorSwitch) def index_model_switch(request): """ Affichage de l'ensemble des modèles de switches""" - model_switch_list = ModelSwitch.objects + model_switch_list = ModelSwitch.objects.select_related('constructor') constructor_switch_list = ConstructorSwitch.objects model_switch_list = SortTable.sort( model_switch_list, diff --git a/users/models.py b/users/models.py index 19fca2c3..c35910ab 100644 --- a/users/models.py +++ b/users/models.py @@ -530,20 +530,19 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): def notif_inscription(self): """ Prend en argument un objet user, envoie un mail de bienvenue """ template = loader.get_template('users/email_welcome') - assooptions, _created = AssoOption.objects.get_or_create() mailmessageoptions, _created = MailMessageOption\ .objects.get_or_create() context = Context({ 'nom': self.get_full_name(), - 'asso_name': assooptions.name, - 'asso_email': assooptions.contact, + 'asso_name': AssoOption.get_cached_value('name'), + 'asso_email': AssoOption.get_cached_value('contact'), 'welcome_mail_fr': mailmessageoptions.welcome_mail_fr, 'welcome_mail_en': mailmessageoptions.welcome_mail_en, 'pseudo': self.pseudo, }) send_mail( 'Bienvenue au %(name)s / Welcome to %(name)s' % { - 'name': assooptions.name + 'name': AssoOption.get_cached_value('name') }, '', GeneralOption.get_cached_value('email_from'), @@ -560,11 +559,10 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): req.user = self req.save() template = loader.get_template('users/email_passwd_request') - options, _created = AssoOption.objects.get_or_create() context = { 'name': req.user.get_full_name(), - 'asso': options.name, - 'asso_mail': options.contact, + 'asso': AssoOption.get_cached_value('name'), + 'asso_mail': AssoOption.get_cached_value('contact'), 'site_name': GeneralOption.get_cached_value('site_name'), 'url': request.build_absolute_uri( reverse('users:process', kwargs={'token': req.token})), @@ -572,7 +570,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): } send_mail( 'Changement de mot de passe du %(name)s / Password\ - renewal for %(name)s' % {'name': options.name}, + renewal for %(name)s' % {'name': AssoOption.get_cached_value('name')}, template.render(context), GeneralOption.get_cached_value('email_from'), [req.user.email], @@ -617,13 +615,12 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): """Notification mail lorsque une machine est automatiquement ajoutée par le radius""" template = loader.get_template('users/email_auto_newmachine') - assooptions, _created = AssoOption.objects.get_or_create() context = Context({ 'nom': self.get_full_name(), 'mac_address' : interface.mac_address, - 'asso_name': assooptions.name, + 'asso_name': AssoOption.get_cached_value('name'), 'interface_name' : interface.domain, - 'asso_email': assooptions.contact, + 'asso_email': AssoOption.get_cached_value('contact'), 'pseudo': self.pseudo, }) send_mail( @@ -1260,12 +1257,11 @@ class Ban(models.Model): def notif_ban(self): """ Prend en argument un objet ban, envoie un mail de notification """ template = loader.get_template('users/email_ban_notif') - options, _created = AssoOption.objects.get_or_create() context = Context({ 'name': self.user.get_full_name(), 'raison': self.raison, 'date_end': self.date_end, - 'asso_name': options.name, + 'asso_name': AssoOption.get_cached_value('name'), }) send_mail( 'Deconnexion disciplinaire', diff --git a/users/views.py b/users/views.py index 11da6843..9b4b818a 100644 --- a/users/views.py +++ b/users/views.py @@ -777,8 +777,7 @@ def profil(request, users, userid): SortTable.USERS_INDEX_WHITE ) user_solde = OptionalUser.get_cached_value('user_solde') - options, _created = AssoOption.objects.get_or_create() - allow_online_payment = options.payment != 'NONE' + allow_online_payment = AssoOption.get_cached_value('payment') != 'NONE' return render( request, 'users/profil.html', From 9f560b21414724011a46e74dd0c84088fb3bcfa4 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 31 Jan 2018 04:10:10 +0100 Subject: [PATCH 4/5] Mise en cache de l'instance en entier, supprime les clefs foireuses --- preferences/models.py | 117 +++++++++++++++++++++++++----------------- topologie/views.py | 3 +- users/models.py | 3 +- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/preferences/models.py b/preferences/models.py index 6bc101d1..591b783e 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -69,18 +69,17 @@ class OptionalUser(models.Model): ) @classmethod - def set_in_cache(cls, key): - machine_options, _created = cls.objects.get_or_create() - value = getattr(machine_options, key) - cache.set('optionaluser_' + key, value, None) - return value + def set_in_cache(cls): + optionaluser, _created = cls.objects.get_or_create() + cache.set('optionaluser', optionaluser, None) + return optionaluser @classmethod def get_cached_value(cls, key): - value = cache.get('optionaluser_' + key) - if value == None: - value = cls.set_in_cache(key) - return value + optionaluser = cache.get('optionaluser') + if optionaluser == None: + optionaluser = cls.set_in_cache() + return getattr(optionaluser, key) class Meta: permissions = ( @@ -148,6 +147,13 @@ class OptionalUser(models.Model): c.save() +@receiver(post_save, sender=OptionalUser) +def optionaluser_post_save(sender, **kwargs): + """Ecriture dans le cache""" + user_pref = kwargs['instance'] + user_pref.set_in_cache() + + class OptionalMachine(models.Model): """Options pour les machines : maximum de machines ou d'alias par user sans droit, activation de l'ipv6""" @@ -176,18 +182,17 @@ class OptionalMachine(models.Model): return not self.get_cached_value('ipv6_mode') == 'DISABLED' @classmethod - def set_in_cache(cls, key): - machine_options, _created = cls.objects.get_or_create() - value = getattr(machine_options, key) - cache.set('optionalmachine_' + key, value, None) - return value + def set_in_cache(cls): + optionalmachine, _created = cls.objects.get_or_create() + cache.set('optionalmachine', optionalmachine, None) + return optionalmachine @classmethod def get_cached_value(cls, key): - value = cache.get('optionalmachine_' + key) - if value == None: - value = cls.set_in_cache(key) - return value + optionalmachine = cache.get('optionalmachine') + if optionalmachine == None: + optionalmachine = cls.set_in_cache() + return getattr(optionalmachine, key) class Meta: permissions = ( @@ -250,9 +255,9 @@ class OptionalMachine(models.Model): @receiver(post_save, sender=OptionalMachine) def optionalmachine_post_save(sender, **kwargs): - """Synchronisation ipv6""" + """Synchronisation ipv6 et ecriture dans le cache""" machine_pref = kwargs['instance'] - + machine_pref.set_in_cache() if machine_pref.ipv6_mode != "DISABLED": for interface in machines.models.Interface.objects.all(): interface.sync_ipv6() @@ -291,18 +296,17 @@ class OptionalTopologie(models.Model): ) @classmethod - def set_in_cache(cls, key): - machine_options, _created = cls.objects.get_or_create() - value = getattr(machine_options, key) - cache.set('optionaltopologie_' + key, value, None) - return value + def set_in_cache(cls): + optionaltopologie, _created = cls.objects.get_or_create() + cache.set('optionaltopologie', optionaltopologie, None) + return optionaltopologie @classmethod def get_cached_value(cls, key): - value = cache.get('optionaltopologie_' + key) - if value == None: - value = cls.set_in_cache(key) - return value + optionaltopologie = cache.get('optionaltopologie') + if optionaltopologie == None: + optionaltopologie = cls.set_in_cache() + return getattr(optionaltopologie, key) class Meta: permissions = ( @@ -362,6 +366,13 @@ class OptionalTopologie(models.Model): de voir les préférences concernant la topologie" +@receiver(post_save, sender=OptionalTopologie) +def optionaltopologie_post_save(sender, **kwargs): + """Ecriture dans le cache""" + topologie_pref = kwargs['instance'] + topologie_pref.set_in_cache() + + class GeneralOption(models.Model): """Options générales : nombre de resultats par page, nom du site, temps où les liens sont valides""" @@ -390,18 +401,17 @@ class GeneralOption(models.Model): ) @classmethod - def set_in_cache(cls, key): - machine_options, _created = cls.objects.get_or_create() - value = getattr(machine_options, key) - cache.set('generaloption_' + key, value, None) - return value + def set_in_cache(cls): + generaloption, _created = cls.objects.get_or_create() + cache.set('generaloption', generaloption, None) + return generaloption @classmethod def get_cached_value(cls, key): - value = cache.get('generaloption_' + key) - if value == None: - value = cls.set_in_cache(key) - return value + generaloption = cache.get('generaloption') + if generaloption == None: + generaloption = cls.set_in_cache() + return getattr(generaloption, key) class Meta: permissions = ( @@ -462,6 +472,13 @@ class GeneralOption(models.Model): de voir les préférences générales" +@receiver(post_save, sender=GeneralOption) +def generaloption_post_save(sender, **kwargs): + """Ecriture dans le cache""" + general_pref = kwargs['instance'] + general_pref.set_in_cache() + + class Service(models.Model): """Liste des services affichés sur la page d'accueil : url, description, image et nom""" @@ -572,18 +589,17 @@ class AssoOption(models.Model): ) @classmethod - def set_in_cache(cls, key): - machine_options, _created = cls.objects.get_or_create() - value = getattr(machine_options, key) - cache.set('assooption_' + key, value, None) - return value + def set_in_cache(cls): + assooption, _created = cls.objects.get_or_create() + cache.set('assooption', assooption, None) + return assooption @classmethod def get_cached_value(cls, key): - value = cache.get('assooption_' + key) - if value == None: - value = cls.set_in_cache(key) - return value + assooption = cache.get('assooption') + if assooption == None: + assooption = cls.set_in_cache() + return getattr(assooption, key) class Meta: permissions = ( @@ -643,6 +659,13 @@ class AssoOption(models.Model): de voir les préférences concernant l'association" +@receiver(post_save, sender=AssoOption) +def assooption_post_save(sender, **kwargs): + """Ecriture dans le cache""" + asso_pref = kwargs['instance'] + asso_pref.set_in_cache() + + class MailMessageOption(models.Model): """Reglages, mail de bienvenue et autre""" PRETTY_NAME = "Options de corps de mail" diff --git a/topologie/views.py b/topologie/views.py index cc129f42..63d13b0c 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -364,8 +364,7 @@ def new_switch(request): request.POST or None, ) if switch.is_valid() and machine.is_valid() and interface.is_valid(): - options, _created = AssoOption.objects.get_or_create() - user = options.utilisateur_asso + user = AssoOption.get_cached_value('utilisateur_asso') if not user: messages.error(request, "L'user association n'existe pas encore,\ veuillez le créer ou le linker dans preferences") diff --git a/users/models.py b/users/models.py index c35910ab..eb6b835c 100644 --- a/users/models.py +++ b/users/models.py @@ -701,7 +701,6 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): else: return False, u"Vous n'avez pas le droit d'éditer ce club" else: - options, _created = AssoOption.objects.get_or_create() if self == user_request: return True, None elif user_request.has_perm('users.change_all_users'): @@ -709,7 +708,7 @@ class User(FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin): elif user_request.has_perm('users.change_user'): if self.groups.filter(listright__critical=True): return False, u"Utilisateurs avec droits critiques, ne peut etre édité" - elif self == options.utilisateur_asso: + elif self == AssoOption.get_cached_value('utilisateur_asso'): return False, u"Impossible d'éditer l'utilisateur asso sans droit change_all_users" else: return True, None From a53f69c482facde4c9266c07c95a32744082bdbd Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 31 Jan 2018 04:16:26 +0100 Subject: [PATCH 5/5] Fix fonction de sync ipv6 --- machines/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/machines/models.py b/machines/models.py index afb072a7..16d387a9 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1243,9 +1243,9 @@ class Interface(FieldPermissionModelMixin,models.Model): def sync_ipv6(self): """Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi""" - if machine_options.ipv6_mode == 'SLAAC': + if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC': self.sync_ipv6_slaac() - elif machine_options.ipv6_mode == 'DHCPV6': + elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6': self.sync_ipv6_dhcpv6() else: return