Changeset - 7086387253d4
[Not reviewed]
default
0 5 2
Branko Majic (branko) - 11 years ago 2013-07-13 20:25:42
branko@majic.rs
CONNT-4: Implemented custom ModelForm for Entity. Implemented create view for entities. Added tests for new functionality.
7 files changed with 252 insertions and 1 deletions:
0 comments (0 inline, 0 general)
conntrackt/forms.py
Show inline comments
 
new file 100644
 
# Django imports.
 
from django.forms import ModelForm
 
from django.forms.models import inlineformset_factory
 

	
 
# Application imports.
 
from .models import Entity, Interface
 

	
 

	
 
class EntityForm(ModelForm):
 
    """
 
    Implements a custom model form for entities with some styling changes.
 
    """
 

	
 
    class Meta:
 
        model = Entity
 

	
 
    def __init__(self, *args, **kwargs):
 
        """
 
        Initialises the form instance. Sets-up some bootstrap CSS classes for
 
        widgets.
 
        """
 

	
 
        super(EntityForm, self).__init__(*args, **kwargs)
 

	
 
        # Update the widgets to be wider, and set-up placeholder values for text
 
        # boxes.
 
        self.fields["name"].widget.attrs["class"] = "span6"
 
        self.fields["name"].widget.attrs["placeholder"] = "Entity name"
 
        self.fields["description"].widget.attrs["class"] = "span6"
 
        self.fields["description"].widget.attrs["placeholder"] = "Description for new entity."
 
        self.fields["project"].widget.attrs["class"] = "span6"
 
        self.fields["location"].widget.attrs["class"] = "span6"
conntrackt/templates/conntrackt/entity_create_form.html
Show inline comments
 
new file 100644
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 
{# For Bootstrapped forms #}
 
{% load crispy_forms_tags %}
 

	
 
{% block content %}
 
<div class="row">
 
  <h1 class="span12">Add new entity</h1>
 
</div>
 

	
 
<div class="row">
 
  <div class="span6">
 
    <form action="" method="post">
 
      <div class="controls controls-row">
 
      {% csrf_token %}
 
      {{ form | crispy }}
 
      {{ interface_form | crispy }}
 
      </div>
 
      <div class="controls">
 
        <button type="submit" class="btn btn-primary">Add</button>
 
      </div>
 
    </form>
 
  </div>
 
</div>
 
{% endblock content %}
conntrackt/templates/conntrackt/project_detail.html
Show inline comments
 
{% extends "conntrackt/base.html" %}
 

	
 
{# For html_link #}
 
{% load conntrackt_tags %}
 

	
 
{% block content %}
 
<h1>{{project.name}}</h1>
 
<div class="row">
 
  <h1 class="span12">{{project.name}}</h1>
 
</div>
 

	
 
{% if project.description %}
 
<div class="row">
 
@@ -18,6 +21,9 @@
 
  <div class="span12">
 
    {% html_link "Edit" "project_update" project.id class="btn btn-primary" %}
 
    {% html_link "Remove" "project_delete" project.id class="btn btn-primary" %}
 
    {% with project_id=project.id|slugify %}
 
    {% html_link "Add entity" "entity_create" class="btn btn-primary" get="project="|add:project_id %}
 
    {% endwith %}
 
  </div>
 
</div>
 
<hr>
conntrackt/tests/test_forms.py
Show inline comments
 
# Django imports.
 
from django.test import TestCase
 

	
 
# Application imports.
 
from conntrackt.forms import EntityForm
 

	
 

	
 
class EntityFormTest(TestCase):
 
    """
 
    Tests for the custom Entity model form.
 
    """
 

	
 
    def test_styling(self):
 
        """
 
        Test that the form styling is set-up correctly.
 
        """
 

	
 
        form = EntityForm()
 

	
 
        self.assertIn("span6", form.fields["name"].widget.attrs["class"])
 
        self.assertIn("span6", form.fields["description"].widget.attrs["class"])
 
        self.assertIn("span6", form.fields["project"].widget.attrs["class"])
 
        self.assertIn("span6", form.fields["location"].widget.attrs["class"])
conntrackt/tests/test_views.py
Show inline comments
 
@@ -4,12 +4,14 @@ from zipfile import ZipFile, ZIP_DEFLATE
 

	
 
# Django imports.
 
from django.core.urlresolvers import reverse
 
from django.test import RequestFactory
 
from django.test import TestCase
 
from django.test.client import Client
 
from django.contrib.auth.models import User, Permission
 

	
 
# Application imports
 
from conntrackt.models import Project, Location
 
from conntrackt.views import EntityCreateView
 

	
 

	
 
class ViewTest(TestCase):
 
@@ -746,3 +748,104 @@ class LocationDeleteViewTest(TestCase):
 
                                    follow=True)
 

	
 
        self.assertContains(response, "Location Test Location 1 has been removed.")
 

	
 

	
 
class EntityCreateViewTest(TestCase):
 

	
 
    def setUp(self):
 
        """
 
        Sets-up some data necessary for testing.
 
        """
 

	
 
        # Set-up some data for testing.
 
        Project.objects.create(name="Test Project 1", description="This is test project 1.")
 
        Project.objects.create(name="Test Project 2", description="This is test project 2.")
 
        Location.objects.create(name="Test Location 1", description="This is test location 1.")
 
        Location.objects.create(name="Test Location 2", description="This is test location 2.")
 

	
 
    def test_permission_denied(self):
 
        """
 
        Tests if permission will be denied for client without sufficient privileges.
 
        """
 

	
 
        User.objects.create_user("noperms", "noperms@example.com", "noperms")
 

	
 
        self.client.login(username="noperms", password="noperms")
 

	
 
        response = self.client.get(reverse("entity_create"))
 

	
 
        self.assertContains(response, "You have insufficient privileges to access this resource. Please contact your local system administrator if you believe you should have been granted access.", status_code=403)
 

	
 
    def test_permission_granted(self):
 
        """
 
        Tests if permission will be granted for user with correct privileges.
 
        """
 

	
 
        user = User.objects.create_user("fullperms", "fullperms@example.com", "fullperms")
 
        user.user_permissions.add(Permission.objects.get(codename="add_entity"))
 

	
 
        self.client.login(username="fullperms", password="fullperms")
 

	
 
        response = self.client.get(reverse("entity_create"))
 

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_form_project_limit(self):
 
        """
 
        Tests if the queryset is properly limitted to specific project if GET
 
        parameters is passed.
 
        """
 

	
 
        # Set-up the view.
 
        view = EntityCreateView()
 
        view.request = RequestFactory().get("/fake-path?project=1")
 
        view.object = None
 

	
 
        # Get the form.
 
        form = view.get_form(view.get_form_class())
 

	
 
        self.assertQuerysetEqual(form.fields["project"].queryset, ["<Project: Test Project 1>"])
 

	
 
    def test_form_location_limit(self):
 
        """
 
        Tests if the queryset is properly limitted to specific location if GET
 
        parameters is passed.
 
        """
 

	
 
        # Set-up the view.
 
        view = EntityCreateView()
 
        view.request = RequestFactory().get("/fake-path?location=1")
 
        view.object = None
 

	
 
        # Get the form.
 
        form = view.get_form(view.get_form_class())
 

	
 
        self.assertQuerysetEqual(form.fields["location"].queryset, ["<Location: Test Location 1>"])
 

	
 
    def test_initial_project(self):
 
        """
 
        Tests if the choice field for project is defaulted to project passed as
 
        part of GET parameters.
 
        """
 
        
 
        view = EntityCreateView()
 
        view.request = RequestFactory().get("/fake-path?project=1")
 
        view.object = None
 

	
 
        initial = view.get_initial()
 

	
 
        self.assertDictContainsSubset({"project": "1"}, initial)
 

	
 
    def test_initial_location(self):
 
        """
 
        Tests if the choice field for location is defaulted to location passed
 
        as part of GET parameters.
 
        """
 
        
 
        view = EntityCreateView()
 
        view.request = RequestFactory().get("/fake-path?location=1")
 
        view.object = None
 

	
 
        initial = view.get_initial()
 

	
 
        self.assertDictContainsSubset({"location": "1"}, initial)
conntrackt/urls.py
Show inline comments
 
@@ -6,6 +6,7 @@ from django.contrib.auth.views import lo
 
from .views import IndexView, EntityView, entity_iptables, project_iptables
 
from .views import ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView
 
from .views import LocationCreateView, LocationUpdateView, LocationDeleteView
 
from .views import EntityCreateView
 

	
 

	
 
urlpatterns = patterns(
 
@@ -34,6 +35,9 @@ urlpatterns = patterns(
 
    # View for showing information about an entity.
 
    url(r'^entity/(?P<pk>\d+)/$', EntityView.as_view(),
 
        name='entity'),
 
    # View for creating a new entity.
 
    url(r'^entity/add/$', EntityCreateView.as_view(), name="entity_create"),
 

	
 
    # View for rendering iptables rules for a specific entity.
 
    url(r'^entity/(?P<pk>\d+)/iptables/$', entity_iptables, name="entity_iptables"),
 
    # View for rendering zip file with iptables rules for all entities in a project.
conntrackt/views.py
Show inline comments
 
@@ -14,6 +14,7 @@ from django.views.generic import Templat
 
from braces.views import MultiplePermissionsRequiredMixin
 

	
 
# Application imports.
 
from .forms import EntityForm
 
from .models import Project, Entity, Location
 
from .utils import generate_entity_iptables
 

	
 
@@ -417,3 +418,58 @@ class LocationDeleteView(MultiplePermiss
 
        messages.success(self.request, "Location %s has been removed." % self.get_object().name, extra_tags="alert alert-success")
 

	
 
        return super(LocationDeleteView, self).post(*args, **kwargs)
 

	
 

	
 
class EntityCreateView(MultiplePermissionsRequiredMixin, CreateView):
 
    """
 
    View for creating a new entity.
 
    """
 

	
 
    model = Entity
 
    form_class = EntityForm
 
    template_name_suffix = "_create_form"
 

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

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

	
 
    def get_form(self, form_class):
 
        """
 
        Returns an instance of form that can be used by the view.
 

	
 
        The method will limit the project or location select inputs if request
 
        contained this information.
 
        """
 

	
 
        form = super(EntityCreateView, self).get_form(form_class)
 

	
 
        # Limit the project selection if required.
 
        project_id = self.request.GET.get("project", None)
 
        if project_id:
 
            form.fields["project"].queryset = Project.objects.filter(pk=project_id)
 
            form.fields["project"].widget.attrs["readonly"] = True
 

	
 
        # Limit the location selection if required.
 
        location_id = self.request.GET.get("location", None)
 
        if location_id:
 
            form.fields["location"].queryset = Location.objects.filter(pk=location_id)
 
            form.fields["location"].widget.attrs["readonly"] = True
 
        
 
        return form
 

	
 
    def get_initial(self):
 
        """
 
        Returns initial values that should be pre-selected (if they were
 
        specified through a GET parameter).
 
        """
 

	
 
        initial = super(EntityCreateView, self).get_initial()
 

	
 
        initial["project"] = self.request.GET.get("project", None)
 
        initial["location"] = self.request.GET.get("location", None)
 

	
 
        return initial
0 comments (0 inline, 0 general)