Changeset - 2a0c8cb4797c
[Not reviewed]
default
0 5 1
Branko Majic (branko) - 11 years ago 2013-07-17 20:21:42
branko@majic.rs
CONNT-5: Added custom model form for Interface. Applied small improvement to EntityForm for styling. Implemented adding interfaces to entities from the entity details page. Updated representation of interfaces on entity details page. Updated view tests for the new functionality. Minor fix to EntityView.
6 files changed with 206 insertions and 17 deletions:
0 comments (0 inline, 0 general)
conntrackt/forms.py
Show inline comments
 
@@ -22,11 +22,37 @@ class EntityForm(ModelForm):
 

	
 
        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"
 
        # Update the widgets to be wider.
 
        for field_name, field in self.fields.iteritems():
 
            field.widget.attrs["class"] = "span6"
 

	
 
        # Set-up some placeholders.
 
        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"
 
        self.fields["description"].widget.attrs["placeholder"] = "Entity description"
 

	
 

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

	
 
    class Meta:
 
        model = Interface
 

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

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

	
 
        # Update the widgets to be wider.
 
        for field_name, field in self.fields.iteritems():
 
            field.widget.attrs["class"] = "span6"
 

	
 
        # Set-up some placeholders.
 
        self.fields["name"].widget.attrs["placeholder"] = "Interface name"
 
        self.fields["description"].widget.attrs["placeholder"] = "Interface description"
 
        self.fields["address"].widget.attrs["placeholder"] = "IP address of interface"
 
        self.fields["netmask"].widget.attrs["placeholder"] = "IP address netmask"
conntrackt/templates/conntrackt/entity_detail.html
Show inline comments
 
@@ -24,6 +24,9 @@
 
    {% html_link "Edit" "entity_update" entity.id class="btn btn-primary" %}
 
    {% html_link "Remove" "entity_delete" entity.id class="btn btn-primary" %}
 
    {% html_link "Get Iptables" 'entity_iptables' entity.id class="btn btn-primary" %}
 
    {% with entity_id=entity.id|slugify %}
 
    {% html_link "Add interface" "interface_create" class="btn btn-primary" get="entity="|add:entity_id %}
 
    {% endwith %}
 
  </div>
 
</div>
 

	
 
@@ -53,11 +56,11 @@
 
        <tr>
 
          <th style="width:99%">Interfaces</th>
 
        </tr>
 
        <tr>
 
          {% for interface in interfaces %}
 
        {% for interface in interfaces %}
 
          <tr>
 
            <td>{{interface.name}} ({{interface.address}}/{{interface.netmask}})</td>
 
          {% endfor %}
 
        </tr>
 
          </tr>
 
        {% endfor %}
 
      </table>
 
    </div>
 
  </div>
conntrackt/templates/conntrackt/interface_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 interface</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/tests/test_views.py
Show inline comments
 
@@ -10,8 +10,8 @@ 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
 
from conntrackt.models import Project, Location, Entity
 
from conntrackt.views import EntityCreateView, InterfaceCreateView
 

	
 

	
 
class ViewTest(TestCase):
 
@@ -992,3 +992,75 @@ class EntityUpdateViewTest(TestCase):
 
        self.assertContains(response, ">Edit entity Test Entity 1<")
 
        self.assertContains(response, 'value="Test Entity 1"')
 
        self.assertContains(response, "This is a test entity 1.")
 

	
 

	
 
class InterfaceCreateViewTest(TestCase):
 

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

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

	
 
    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("interface_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_interface"))
 

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

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

	
 
        self.assertEqual(response.status_code, 200)
 

	
 
    def test_form_entity_limit(self):
 
        """
 
        Tests if the queryset is properly limitted to specific entity if GET
 
        parameter is passed.
 
        """
 

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

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

	
 
        self.assertQuerysetEqual(form.fields["entity"].queryset, ["<Entity: Test Entity 1 (Test Project - Test Location)>"])
 

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

	
 
        initial = view.get_initial()
 

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

	
conntrackt/urls.py
Show inline comments
 
@@ -7,6 +7,7 @@ from .views import IndexView, EntityView
 
from .views import ProjectView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView
 
from .views import LocationCreateView, LocationUpdateView, LocationDeleteView
 
from .views import EntityCreateView, EntityUpdateView, EntityDeleteView
 
from .views import InterfaceCreateView
 

	
 

	
 
urlpatterns = patterns(
 
@@ -42,6 +43,9 @@ urlpatterns = patterns(
 
    # View for deleting an entity.
 
    url(r'^entity/(?P<pk>\d+)/remove/$', EntityDeleteView.as_view(), name="entity_delete"),
 

	
 
    # View for creating a new interface.
 
    url(r'^interface/add/$', InterfaceCreateView.as_view(), name="interface_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,8 +14,8 @@ from django.views.generic import Templat
 
from braces.views import MultiplePermissionsRequiredMixin
 

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

	
 

	
 
@@ -126,7 +126,7 @@ class EntityView(MultiplePermissionsRequ
 
        """
 

	
 
        # Call the parent class method.
 
        context = super(DetailView, self).get_context_data(**kwargs)
 
        context = super(EntityView, self).get_context_data(**kwargs)
 

	
 
        # Add the rendered iptables rules to the context.
 
        context['entity_iptables'] = generate_entity_iptables(self.object)
 
@@ -529,7 +529,6 @@ class EntityDeleteView(MultiplePermissio
 

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

	
 

	
 
    def delete(self, *args, **kwargs):
 
        """
 
        Deletes the object. This method is overridden in order to obtain the
 
@@ -541,3 +540,61 @@ class EntityDeleteView(MultiplePermissio
 
        self.success_url = reverse("project", args=(self.get_object().project.id,))
 

	
 
        return super(EntityDeleteView, self).delete(*args, **kwargs)
 

	
 

	
 
class InterfaceCreateView(MultiplePermissionsRequiredMixin, CreateView):
 
    """
 
    View for creating a new interface.
 
    """
 

	
 
    model = Interface
 
    form_class = InterfaceForm
 
    template_name_suffix = "_create_form"
 

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

	
 
    # 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 entity select input if request contained this
 
        information.
 
        """
 

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

	
 
        # Limit the entity selection if required.
 
        entity_id = self.request.GET.get("entity", None)
 
        if entity_id:
 
            form.fields["entity"].queryset = Entity.objects.filter(pk=entity_id)
 
            form.fields["entity"].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(InterfaceCreateView, self).get_initial()
 

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

	
 
        return initial
 

	
 
    def get_success_url(self):
 
        """
 
        Returns the URL to which the user should be redirected after an
 
        interface has been created.
 

	
 
        The URL in this case will be set to entity's details page.
 
        """
 

	
 
        return reverse("entity", args=(self.object.entity.pk,))
0 comments (0 inline, 0 general)