Changeset - 843dbaebdae4
[Not reviewed]
default
0 4 1
Branko Majic (branko) - 10 years ago 2013-10-19 12:57:09
branko@majic.rs
CONNT-19: Implemented search over names and descriptions.
5 files changed with 138 insertions and 0 deletions:
0 comments (0 inline, 0 general)
conntrackt/models.py
Show inline comments
 
@@ -20,12 +20,34 @@
 

	
 

	
 
# Django imports.
 
from django.core.exceptions import ValidationError
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.db.models.query_utils import Q
 

	
 

	
 
class SearchManager(models.Manager):
 
    """
 
    Custom model manager that implements search for model instances that contain
 
    a specific string (search term) in fields "name" or "description".
 
    """
 

	
 
    def search(self, search_term):
 
        """
 
        Performs a search for model instances that contain the provided search
 
        term in fields "name" or "description". The search is case-insensitive.
 

	
 
        Arguments:
 
          search_term - String to search the name and description for.
 

	
 
        Returns:
 
          Query set with model instances that matched the search.
 
        """
 

	
 
        return self.filter(Q(name__icontains=search_term) | Q(description__icontains=search_term))
 

	
 

	
 
class Project(models.Model):
 
    """
 
    Implements a model with information about a project. A project has some
 
    basic settings, and mainly serves the purpose of grouping entities for
 
@@ -36,12 +58,13 @@ class Project(models.Model):
 
      name - String denoting the project name.
 
      description - Free-form description of the project.
 
    """
 

	
 
    name = models.CharField(max_length=100, unique=True)
 
    description = models.TextField(blank=True)
 
    objects = SearchManager()
 

	
 
    class Meta:
 
        permissions = (("view", "Can view information"),)
 

	
 
    def __unicode__(self):
 
        """
 
@@ -116,12 +139,13 @@ class Entity(models.Model):
 
    """
 

	
 
    name = models.CharField(max_length=100)
 
    description = models.TextField(blank=True)
 
    project = models.ForeignKey(Project)
 
    location = models.ForeignKey(Location)
 
    objects = SearchManager()
 

	
 
    class Meta:
 
        # Fix the plural form used by Django.
 
        verbose_name_plural = 'entities'
 
        # Enforce uniqueness of entity name in a project.
 
        unique_together = ("name", "project")
conntrackt/templates/conntrackt/base.html
Show inline comments
 
@@ -31,12 +31,20 @@
 
              {% block header %}
 
            <ul class="nav">
 
              <li class="{% active_link 'index' %}"><a href="{% url "index" %}"><i class="icon-home icon-white"></i> Main Page</a></li>
 
              <li class="{% active_link 'admin' %}"><a href="{% url "admin:app_list" "conntrackt" %}"><i class="icon-wrench icon-white"></i> Administration</a></li>
 
            </ul>
 
            <ul class="nav pull-right">
 
              <li>
 
                <form action="{% url "search" %}" class="navbar-search pull-left" method="GET">
 
                  <div class="input-prepend">
 
                    <button type="submit" class="btn btn-link"><span class="icon-search icon-white"></span></button>
 
                    <input class="span2 search-query" type="text" name="q"  placeholder="Search">
 
                  </div>
 
                </form>
 
              </li>
 
              {% if user.is_anonymous %}
 
              <li>{% html_link '<i class="icon-user icon-white"></i> Log-in' 'login' get="next="|add:request.path %}</li>
 
              {% else %}
 
              <li><a href=""><i class="icon-user icon-white"></i> {{user}}</a></li>
 
              <li>{% html_link '<i class="icon-off icon-white"></i> Log-out' "logout" get="next="|add:request.path %}</li>
 
              {% endif %}
conntrackt/templates/conntrackt/search.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{% block content %}
 

	
 
<div class="row">
 
  <div class="span12">
 
    {% if search_term %}
 
      <h1>Search results for: <strong>{{ search_term }}</strong></h1>
 
    {% else %}
 
      <h1>Search</h1>
 
    {% endif %}
 
  </div>
 

	
 
  <div class="span12">
 
    <hr>
 
    <form action="{% url "search" %}" class="form-search" method="GET">
 
      <input class="search-query" type="text" name="q"  placeholder="Search" value="{{ search_term }}"/>
 
      <button type="submit" class="btn"><span class="icon-search"></span> Search</button>
 
    </form>
 
    <hr>
 

	
 
    {% if projects %}
 
      <h2><small>Matched projects</small></h2>
 
      <ul class="unstyled">
 
        {% for project in projects %}
 
          <li><a href="{{ project.get_absolute_url }}">{{ project.name }}</a></li>
 
        {% endfor %}
 
      </ul>
 
      <hr>
 
    {% elif search_term %}
 
      <p>There are no projects matching your query.</p>
 
      <hr>
 
    {% endif %}
 

	
 
    {% if entities %}
 
      <h2><small>Matched Entities</small></h2>
 
      <ul class="unstyled">
 
        {% for entity in entities %}
 
          <li><a href="{{ entity.get_absolute_url }}">{{ entity.name }}</a> (from {{ entity.project.name }})</li>
 
        {% endfor %}
 
      </ul>
 
      <hr>
 
    {% elif search_term %}
 
      <p>There are no entities matching your query.</p>
 
      <hr>
 
    {% endif %}
 

	
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/urls.py
Show inline comments
 
@@ -27,12 +27,13 @@ from django.contrib.auth.views import lo
 
from .views import IndexView, EntityView, entity_iptables, project_iptables, project_diagram
 
from .views import ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView
 
from .views import LocationCreateView, LocationUpdateView, LocationDeleteView
 
from .views import EntityCreateView, EntityUpdateView, EntityDeleteView
 
from .views import InterfaceCreateView, InterfaceUpdateView, InterfaceDeleteView
 
from .views import CommunicationCreateView, CommunicationUpdateView, CommunicationDeleteView
 
from .views import SearchView
 

	
 

	
 
urlpatterns = patterns(
 
    'conntrackt.views',
 
    # Homepage/index view.
 
    url(r'^$', IndexView.as_view(), name="index"),
 
@@ -88,7 +89,10 @@ urlpatterns = patterns(
 
    # View for showing project communications in a diagram.
 
    url(r'^project/(?P<pk>\d+)/diagram/$', project_diagram, name="project_diagram"),
 

	
 
    # Views for logging-in/out the users.
 
    url(r'^login/$', login, {'template_name': 'conntrackt/login.html'}, name="login"),
 
    url(r'^logout/$', logout, name="logout"),
 

	
 
    # View for displaying the search page.
 
    url(r'^search/$', SearchView.as_view(), name="search"),
 
)
conntrackt/views.py
Show inline comments
 
@@ -24,12 +24,13 @@ from StringIO import StringIO
 
from zipfile import ZipFile, ZIP_DEFLATED
 

	
 
# Django imports.
 
from django.contrib.auth.decorators import permission_required
 
from django.contrib import messages
 
from django.core.urlresolvers import reverse, reverse_lazy
 
from django.db.models import Q
 
from django.http import HttpResponse
 
from django.shortcuts import render_to_response, get_object_or_404
 
from django.views.generic import TemplateView, DetailView, CreateView, UpdateView, DeleteView
 

	
 
# Third-party application imports.
 
from braces.views import MultiplePermissionsRequiredMixin, SetHeadlineMixin
 
@@ -962,6 +963,57 @@ def project_diagram(request, pk):
 

	
 
    # Set the mime type.
 
    response = HttpResponse(content, mimetype='image/svg+xml')
 

	
 
    # Return the response object.
 
    return response
 

	
 

	
 
class SearchView(MultiplePermissionsRequiredMixin, TemplateView):
 
    """
 
    Custom view used for rendering the search (results) page.
 
    """
 

	
 
    template_name = 'conntrackt/search.html'
 

	
 
    # Required permissions.
 
    permissions = {
 
        "all": ("conntrackt.view",),
 
        }
 

	
 
    # Raise authorisation denied exception for unmet permissions.
 
    raise_exception = True
 

	
 
    def get_context_data(self, **kwargs):
 
        """
 
        Returns the context data that should be used for rendering of the
 
        template.
 

	
 
        Adds context objects:
 
          - 'entities', which is a list of entities that had the search term in
 
            their name or description.
 
          - 'projects', which is a list of entities that had the search term in
 
            their name or description.
 
          - 'search_term', which is a string of previous query that brought the
 
            user to page (if any). The term will be stripped from leading and
 
            trailing spaces/tabs.
 
        """
 

	
 
        # Set the context using the parent aclass.
 
        context = super(SearchView, self).get_context_data(**kwargs)
 

	
 
        # Retrieve the search term, and strip it if it was provided.
 
        search_term = self.request.GET.get("q", None)
 
        if search_term:
 
            search_term = search_term.strip()
 

	
 
        # Do not allow empty searches.
 
        if search_term == "":
 
            messages.error(self.request, "Search query is not allowed to be empty.", extra_tags="alert alert-error")
 
        # Set-up the context objects if search was sent. Otherwise empty search
 
        # page will be shown.
 
        elif search_term is not None:
 
            context['search_term'] = search_term
 
            context['entities'] = Entity.objects.search(search_term)
 
            context['projects'] = Project.objects.search(search_term)
 

	
 
        return context
0 comments (0 inline, 0 general)