savannah-cvs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Savannah-cvs] [SCM] Savane-cleanup framework branch, master, updated. 4


From: Sylvain Beucler
Subject: [Savannah-cvs] [SCM] Savane-cleanup framework branch, master, updated. 44f900b041530018c7b0b0e0626087a0965924d3
Date: Fri, 23 Jul 2010 21:25:45 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "Savane-cleanup framework".

The branch, master has been updated
       via  44f900b041530018c7b0b0e0626087a0965924d3 (commit)
      from  e013d40dcd0dc22d1ddab9ff7189d96b89bde3ea (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.savannah.gnu.org/cgit/savane-cleanup/framework.git/commit/?id=44f900b041530018c7b0b0e0626087a0965924d3

commit 44f900b041530018c7b0b0e0626087a0965924d3
Author: Sylvain Beucler <address@hidden>
Date:   Fri Jul 23 23:25:39 2010 +0200

    Add list of users and groups

diff --git a/savane/filters.py b/savane/filters.py
new file mode 100644
index 0000000..da1b7a4
--- /dev/null
+++ b/savane/filters.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+# 
+# Copyright (C) 2010  Sylvain Beucler
+# Copyright ??? Django team
+#
+# This file is part of Savane.
+# 
+# Savane is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+# 
+# Savane is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from django.db import models
+import operator
+from django.http import HttpResponse
+
+# Copy/paste these:
+#from django.contrib.admin.views.main import
+#ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR,
+#SEARCH_VAR, TO_FIELD_VAR, IS_POPUP_VAR, ERROR_FLAG
+ALL_VAR = 'all'
+ORDER_VAR = 'o'
+ORDER_TYPE_VAR = 'ot'
+PAGE_VAR = 'p'
+SEARCH_VAR = 'q'
+TO_FIELD_VAR = 't'
+IS_POPUP_VAR = 'pop'
+ERROR_FLAG = 'e'
+
+class ChangeList:
+    """
+    Object to pass views configuration to (e.g.: search string, ordering...)
+    -Draft-
+    """
+    def __init__(model_admin, request):
+        self.query = request.GET.get(SEARCH_VAR, '')
+        self.list_display = model_admin.list_display
+
+def search(f):
+    """
+    Inspired by Django's admin interface, filter queryset based on GET
+    parameters (contrib.admin.views.main.*_VAR):
+
+    - o=N: order by ModelAdmin.display_fields[N]
+    - ot=xxx: order type: 'asc' or 'desc'
+    - q=xxx: plain text search on ModelAdmin.search_fields (^ -> istartswith, 
= -> iexact, @ -> search, each word ANDed)
+    - everything else: name of a Q filter
+
+    exceptions:
+    - p=N: current page
+    - all=: disable pagination
+    - pop: popup
+    - e: error
+    - to: ? (related to making JS-friendly PK values?)
+
+    additional exclusions:
+    - page: used by django.views.generic.list_detail
+
+    We could also try and deduce filters from the Model, or avoid
+    using some declared parameters as Q filters, or find a better
+    idea.
+    """
+    def _decorator(request, *args, **kwargs):
+        qs = kwargs['queryset']
+        model_admin = kwargs['model_admin']
+
+        lookup_params = request.GET.copy()
+        for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, 
IS_POPUP_VAR, 'page'):
+            if lookup_params.has_key(i):
+                del lookup_params[i]
+
+        try:
+            qs = qs.filter(**lookup_params)
+        # Naked except! Because we don't have any other way of validating 
"params".
+        # They might be invalid if the keyword arguments are incorrect, or if 
the
+        # values are not in the correct type, so we might get FieldError, 
ValueError,
+        # ValicationError, or ? from a custom field that raises yet something 
else 
+        # when handed impossible data.
+        except:
+            return HttpResponse("Erreur: paramètres de recherche invalides.")
+            #raise IncorrectLookupParameters
+
+        # TODO: order - but maybe in another, separate filter?
+
+        ##
+        # Search string
+        ##
+        def construct_search(field_name):
+            if field_name.startswith('^'):
+                return "%s__istartswith" % field_name[1:]
+            elif field_name.startswith('='):
+                return "%s__iexact" % field_name[1:]
+            elif field_name.startswith('@'):
+                return "%s__search" % field_name[1:]
+            else:
+                return "%s__icontains" % field_name
+
+        query = request.GET.get(SEARCH_VAR, '')
+        search_fields = model_admin.search_fields
+        if search_fields and query:
+            for bit in query.split():
+                or_queries = [models.Q(**{construct_search(str(field_name)): 
bit}) for field_name in search_fields]
+                qs = qs.filter(reduce(operator.or_, or_queries))
+            for field_name in search_fields:
+                if '__' in field_name:
+                    qs = qs.distinct()
+                    break
+
+        kwargs['queryset'] = qs
+
+        # TODO: pass order params
+        if not kwargs.has_key('extra_context'):
+            kwargs['extra_context'] = {}
+        kwargs['extra_context']['q'] = query
+
+        # TODO: move in a clean-up decorator
+        del kwargs['model_admin']
+        return f(request, *args, **kwargs)
+    return _decorator
diff --git a/savane/svmain/models.py b/savane/svmain/models.py
index 45545d1..41cac1e 100644
--- a/savane/svmain/models.py
+++ b/savane/svmain/models.py
@@ -28,7 +28,7 @@ 
http://www.b-list.org/weblog/2006/jun/06/django-tips-extending-user-model/
 http://mirobetm.blogspot.com/2007/04/en-django-extending-user_2281.html
 
 However profiles were mainly useful in Django < 1.0 where you couldn't
-subclass User as we do.
+subclass User.
 
 Profiles also have a few drawbacks, namely they are site-specific,
 which means you cannot have multiple applications have different
@@ -53,6 +53,11 @@ 
http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inh
 Note that Scott's authentication backend has the same issue than
 profiles: only one profile class can be used on a single website, so
 we don't use it.
+
+The current solution is to use AutoOneToOneField: OneToOneField is
+similar to extending a model class (at the SQL tables level), and
+AutoOneToOneField is a trick from django-annoying to automatically
+create the extended data on first access.
 """
 
 from django.db import models
diff --git a/savane/svmain/urls.py b/savane/svmain/urls.py
index ad690d4..c2e67ca 100644
--- a/savane/svmain/urls.py
+++ b/savane/svmain/urls.py
@@ -18,12 +18,16 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from django.conf.urls.defaults import *
+from django.views.generic.list_detail import object_list, object_detail
 
 import savane.svmain.models as svmain_models
 import django.contrib.auth.models as auth_models
 import views
+from savane.filters import search
 
-urlpatterns = patterns ('',
+urlpatterns = patterns ('',)
+
+urlpatterns += patterns ('',
   url(r'^$', 'django.views.generic.simple.direct_to_template',
       { 'template' : 'index.html',
         'extra_context' : { 'has_left_menu': False } },
@@ -32,30 +36,52 @@ urlpatterns = patterns ('',
       { 'template' : 'svmain/text.html',
         'extra_context' : { 'title' : 'Contact', }, },
       name='contact'),
+)
 
-  # TODO: not sure about the views naming convention - all this
-  # "models in 'svmain', views in 'my'" is getting messy, probably a
-  # mistake from me (Beuc) :P
-  url(r'^p/(?P<slug>[-\w]+)$', 
'django.views.generic.list_detail.object_detail',
-      { 'queryset' : auth_models.Group.objects.all(),
-        'slug_field' : 'name',
-        'template_name' : 'svmain/group_detail.html', },
-      name='savane.svmain.group_detail'),
-  url(r'^pr/(?P<slug>[-\w]+)$', views.group_redir),
-  url(r'^projects/(?P<slug>[-\w]+)$', views.group_redir),
-
-  url(r'^u/(?P<slug>[-\w]+)$', 
'django.views.generic.list_detail.object_detail',
+# TODO: not sure about the views naming convention - all this
+# "models in 'svmain', views in 'my'" is getting messy, probably a
+# mistake from me (Beuc) :P
+from django.contrib.auth.admin import UserAdmin
+urlpatterns += patterns ('',
+  url(r'^u/$',
+      search(object_list),
+      { 'queryset': auth_models.User.objects.all(),
+        'paginate_by': 20,
+        'model_admin': UserAdmin,
+        'extra_context' : { 'title' : 'Users' },
+        'template_name' : 'svmain/user_list.html' },
+      name='savane.svmain.user_list'),
+  url(r'^u/(?P<slug>[-\w]+)$', object_detail,
       { 'queryset' : auth_models.User.objects.all(),
         'slug_field' : 'username',
         'template_name' : 'svmain/user_detail.html', },
       name='savane.svmain.user_detail'),
   url(r'^us/(?P<slug>[-\w]+)$', views.user_redir),
   url(r'^users/(?P<slug>[-\w]+)/?$', views.user_redir),
+)
+
+from django.contrib.auth.admin import GroupAdmin
+urlpatterns += patterns ('',
+  url(r'^p/$',
+      search(object_list),
+      { 'queryset': auth_models.Group.objects.all(),
+        'paginate_by': 20,
+        'model_admin': GroupAdmin,
+        'extra_context' : { 'title' : 'Projects' },
+        'template_name' : 'svmain/group_list.html' },
+      name='savane.svmain.group_list'),
+  url(r'^p/(?P<slug>[-\w]+)$', object_detail,
+      { 'queryset' : auth_models.Group.objects.all(),
+        'slug_field' : 'name',
+        'template_name' : 'svmain/group_detail.html', },
+      name='savane.svmain.group_detail'),
+  url(r'^pr/(?P<slug>[-\w]+)$', views.group_redir),
+  url(r'^projects/(?P<slug>[-\w]+)$', views.group_redir),
 
   url(r'^license/$', 'django.views.generic.list_detail.object_list',
       { 'queryset' : svmain_models.License.objects.all(), },
       name='savane.svmain.license_list'),
-  url(r'^license/(?P<slug>[-\w]+)$', 
'django.views.generic.list_detail.object_detail',
+  url(r'^license/(?P<slug>[-\w]+)$', object_detail,
       { 'queryset' : svmain_models.License.objects.all(), },
       name='savane.svmain.license_detail'),
 )
diff --git a/templates/base.html b/templates/base.html
index 8745260..8823de7 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -27,6 +27,10 @@
         {% endif %}
         <!-- /sitemenu -->
 
+       <li class="menutitle">Search</li>
+        <li class="menuitem"><a href="{% url savane.svmain.user_list 
%}">Users</a></li>
+        <li class="menuitem"><a href="{% url savane.svmain.group_list 
%}">Projects</a></li>
+
        <li class="menutitle">Site help</li>
         <li class="menuitem"><a href="{% url contact %}">Contact us</a></li>
       </ul>
diff --git a/templates/svmain/group_list.html b/templates/svmain/group_list.html
index 7cc235f..0a08990 100644
--- a/templates/svmain/group_list.html
+++ b/templates/svmain/group_list.html
@@ -2,6 +2,12 @@
 
 {% block content %}
 
+{% include "svmain/pagination.inc.html" %}
+
+<form action="." method="GET">
+  Rechercher: <input type="text" name="q" value="{{q}}" />
+</form>
+
 {% if object_list %}
     <ul>
     {% for object in object_list %}
@@ -9,7 +15,7 @@
     {% endfor %}
     </ul>
 {% else %}
-    <p>No group.</p>
+    <p>No groups.</p>
 {% endif %}
 
 {% endblock %}
diff --git a/templates/svmain/pagination.inc.html 
b/templates/svmain/pagination.inc.html
new file mode 100644
index 0000000..d795d27
--- /dev/null
+++ b/templates/svmain/pagination.inc.html
@@ -0,0 +1,33 @@
+{% if page_obj %}
+<div class="pagination">
+    <span class="step-links">
+      {% if paginator.num_pages > 1 %}
+        {% if not page_obj.has_previous %}
+         préc.
+       {% else %}
+          <a href="?page={{ page_obj.previous_page_number }}">préc.</a>
+        {% endif %}
+       &lt;
+       {% for number in paginator.page_range %}
+           {% ifequal number page_obj.number %}
+             {{ number }}
+           {% else %}
+              <a href="?page={{ number }}">{{ number }}</a>
+           {% endifequal %}
+        {% endfor %}
+       &gt;
+        {% if not page_obj.has_next %}
+           suiv.
+       {% else %}
+            <a href="?page={{ page_obj.next_page_number }}">suiv.</a>
+        {% endif %}
+
+        <span class="current">
+            - Page {{ page_obj.number }} sur {{ paginator.num_pages }} 
({{paginator.count}})
+        </span>
+      {% else %}
+       ({{paginator.count}} élément(s))
+      {% endif %}
+    </span>
+</div>
+{% endif %}
diff --git a/templates/svmain/group_list.html b/templates/svmain/user_list.html
similarity index 52%
copy from templates/svmain/group_list.html
copy to templates/svmain/user_list.html
index 7cc235f..1408bf8 100644
--- a/templates/svmain/group_list.html
+++ b/templates/svmain/user_list.html
@@ -2,14 +2,20 @@
 
 {% block content %}
 
+{% include "svmain/pagination.inc.html" %}
+
+<form action="." method="GET">
+  Rechercher: <input type="text" name="q" value="{{q}}" />
+</form>
+
 {% if object_list %}
     <ul>
     {% for object in object_list %}
-        <li><a href="{% url savane.svmain.group_detail object.name %}">{{ 
object.name }}</a></li>
+        <li><a href="{% url savane.svmain.user_detail object.username %}">{{ 
object.username }}</a></li>
     {% endfor %}
     </ul>
 {% else %}
-    <p>No group.</p>
+    <p>No users.</p>
 {% endif %}
 
 {% endblock %}

-----------------------------------------------------------------------

Summary of changes:
 savane/filters.py                                  |  127 ++++++++++++++++++++
 savane/svmain/models.py                            |    7 +-
 savane/svmain/urls.py                              |   54 ++++++--
 templates/base.html                                |    4 +
 templates/svmain/group_list.html                   |    8 +-
 templates/svmain/pagination.inc.html               |   33 +++++
 .../svmain/{group_list.html => user_list.html}     |   10 ++-
 7 files changed, 225 insertions(+), 18 deletions(-)
 create mode 100644 savane/filters.py
 create mode 100644 templates/svmain/pagination.inc.html
 copy templates/svmain/{group_list.html => user_list.html} (52%)


hooks/post-receive
-- 
Savane-cleanup framework



reply via email to

[Prev in Thread] Current Thread [Next in Thread]