Optimisation de order_by('?')
authorEric Mc Sween <eric.mcsween@gmail.com>
Thu, 4 Nov 2010 21:05:58 +0000 (17:05 -0400)
committerEric Mc Sween <eric.mcsween@gmail.com>
Thu, 4 Nov 2010 21:05:58 +0000 (17:05 -0400)
auf_savoirs_en_partage/chercheurs/models.py
auf_savoirs_en_partage/development.py
auf_savoirs_en_partage/savoirs/middleware.py [new file with mode: 0644]
auf_savoirs_en_partage/savoirs/models.py
auf_savoirs_en_partage/savoirs/views.py
auf_savoirs_en_partage/sitotheque/models.py

index 9218216..d460b0d 100644 (file)
@@ -3,7 +3,7 @@ from django.db import models
 from django.db.models import Q
 from datamaster_modeles.models import *
 #from auf_references_modeles.models import Thematique
-from savoirs.models import Discipline
+from savoirs.models import Discipline, RandomQuerySetMixin
 
 GENRE_CHOICES = (('m', 'Homme'), ('f', 'Femme'))
 class Personne(models.Model):
@@ -44,7 +44,7 @@ class ChercheurManager(models.Manager):
     def search_nom(self, nom):
         return self.get_query_set().search_nom(nom)
 
-class ChercheurQuerySet(models.query.QuerySet):
+class ChercheurQuerySet(models.query.QuerySet, RandomQuerySetMixin):
 
     def search(self, text):
         q = None
index 0f28876..a55f3dd 100644 (file)
@@ -4,8 +4,13 @@ TEMPLATE_DEBUG=DEBUG
 INTERNAL_IPS = ('127.0.0.1',)
 AUTH_PASSWORD_REQUIRED = False
 
-# Activer la debug toolbar...
+# Debug toolbar
 
-MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
-INSTALLED_APPS += ('debug_toolbar',)
+# MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
+# INSTALLED_APPS += ('debug_toolbar',)
 # DEBUG_TOOLBAR_CONFIG = dict(INTERCEPT_REDIRECTS=False)
+
+# Profiling
+
+MIDDLEWARE_CLASSES += ('savoirs.middleware.ProfileMiddleware',)
+
diff --git a/auf_savoirs_en_partage/savoirs/middleware.py b/auf_savoirs_en_partage/savoirs/middleware.py
new file mode 100644 (file)
index 0000000..651d9ca
--- /dev/null
@@ -0,0 +1,47 @@
+import sys
+import tempfile
+import hotshot
+import hotshot.stats
+from django.conf import settings
+from cStringIO import StringIO
+
+class ProfileMiddleware(object):
+    """
+    Displays hotshot profiling for any view.
+    http://yoursite.com/yourview/?prof
+
+    Add the "prof" key to query string by appending ?prof (or &prof=)
+    and you'll see the profiling results in your browser.
+    It's set up to only be available in django's debug mode,
+    but you really shouldn't add this middleware to any production configuration.
+    * Only tested on Linux
+    """
+    def process_request(self, request):
+        if settings.DEBUG and request.GET.has_key('prof'):
+            self.tmpfile = tempfile.NamedTemporaryFile()
+            self.prof = hotshot.Profile(self.tmpfile.name)
+
+    def process_view(self, request, callback, callback_args, callback_kwargs):
+        if settings.DEBUG and request.GET.has_key('prof'):
+            return self.prof.runcall(callback, request, *callback_args, **callback_kwargs)
+
+    def process_response(self, request, response):
+        if settings.DEBUG and request.GET.has_key('prof'):
+            self.prof.close()
+
+            out = StringIO()
+            old_stdout = sys.stdout
+            sys.stdout = out
+
+            stats = hotshot.stats.load(self.tmpfile.name)
+            #stats.strip_dirs()
+            stats.sort_stats('cumulative', 'calls')
+            stats.print_stats()
+
+            sys.stdout = old_stdout
+            stats_str = out.getvalue()
+
+            if response and response.content and stats_str:
+                response.content = "<pre>" + stats_str + "</pre>"
+
+        return response
index 2ea3256..0d97fe6 100644 (file)
@@ -1,8 +1,8 @@
 # -*- encoding: utf-8 -*-
-import simplejson, uuid, datetime, caldav, vobject, uuid
+import simplejson, uuid, datetime, caldav, vobject, uuid, random
 from django.contrib.auth.models import User
 from django.db import models
-from django.db.models import Q
+from django.db.models import Q, Max
 from django.db.models.signals import pre_delete
 from timezones.fields import TimeZoneField
 from auf_savoirs_en_partage.backend_config import RESOURCES
@@ -12,6 +12,18 @@ from datamaster_modeles.models import Thematique, Pays, Region
 from lib.calendrier import combine
 from caldav.lib import error
 
+class RandomQuerySetMixin(object):
+    """Mixin pour les modèles.
+       
+    ORDER BY RAND() est très lent sous MySQL. On a besoin d'une autre
+    méthode pour récupérer des objets au hasard.
+    """
+
+    def random(self, n=1):
+        """Récupère aléatoirement un nombre donné d'objets."""
+        ids = random.sample(xrange(self.count()), n)
+        return [self[i] for i in ids]
+
 class Discipline(models.Model):
     id = models.IntegerField(primary_key=True, db_column='id_discipline')
     nom = models.CharField(max_length=765, db_column='nom_discipline')
@@ -38,7 +50,7 @@ class ActualiteManager(models.Manager):
     def search(self, text):
         return self.get_query_set().search(text)
 
-class ActualiteQuerySet(models.query.QuerySet):
+class ActualiteQuerySet(models.query.QuerySet, RandomQuerySetMixin):
 
     def search(self, text):
         q = None
@@ -86,7 +98,7 @@ class EvenementManager(models.Manager):
     def search(self, text):
         return self.get_query_set().search(text)
 
-class EvenementQuerySet(models.query.QuerySet):
+class EvenementQuerySet(models.query.QuerySet, RandomQuerySetMixin):
 
     def search(self, text):
         q = None
@@ -276,7 +288,7 @@ class RecordManager(models.Manager):
     def validated(self):
         return self.get_query_set().validated()
 
-class RecordQuerySet(models.query.QuerySet):
+class RecordQuerySet(models.query.QuerySet, RandomQuerySetMixin):
 
     def search(self, text):
         qs = self
index 406e718..0b74761 100644 (file)
@@ -27,9 +27,9 @@ def index (request):
     actualites = Actualite.objects.filter (visible = '1', date__gt = oldest)
     actualites = actualites[0:configuration['accueil_actualite']]
     evenements = Evenement.objects.filter(approuve=True)[0:configuration['accueil_evenement']]
-    ressources = Record.objects.all().order_by('?')[:configuration['accueil_ressource']]
-    chercheurs = Chercheur.objects.all().order_by('?')[:configuration['accueil_chercheur']]
-    sites = Site.objects.all().order_by('?')[:configuration['accueil_sites']]
+    ressources = Record.objects.all().random(configuration['accueil_ressource'])
+    chercheurs = Chercheur.objects.all().random(configuration['accueil_chercheur'])
+    sites = Site.objects.all().random(configuration['accueil_sites'])
     return render_to_response("savoirs/index.html",
                                dict(actualites=actualites,
                                     evenements=evenements,
index 0026250..f6d3953 100644 (file)
@@ -2,7 +2,7 @@
 from django.db import models
 from django.db.models import Q
 from datamaster_modeles.models import *
-from savoirs.models import Discipline
+from savoirs.models import Discipline, RandomQuerySetMixin
 
 TYPE_SITE_CHOICES = (
     ('RV', 'Revue en ligne'), 
@@ -25,7 +25,7 @@ class SiteManager(models.Manager):
     def search(self, text):
         return self.get_query_set().search(text)
 
-class SiteQuerySet(models.query.QuerySet):
+class SiteQuerySet(models.query.QuerySet, RandomQuerySetMixin):
 
     def search(self, text):
         qs = self