ソースを参照

Collapsed add/edit functionality into ObjectEditView

Jeremy Stretch 9 年 前
コミット
9df86d5268

+ 3 - 3
netbox/circuits/urls.py

@@ -6,7 +6,7 @@ urlpatterns = [
 
     # Providers
     url(r'^providers/$', views.ProviderListView.as_view(), name='provider_list'),
-    url(r'^providers/add/$', views.ProviderAddView.as_view(), name='provider_add'),
+    url(r'^providers/add/$', views.ProviderEditView.as_view(), name='provider_add'),
     url(r'^providers/import/$', views.ProviderBulkImportView.as_view(), name='provider_import'),
     url(r'^providers/edit/$', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
     url(r'^providers/delete/$', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
@@ -16,13 +16,13 @@ urlpatterns = [
 
     # Circuit types
     url(r'^circuit-types/$', views.CircuitTypeListView.as_view(), name='circuittype_list'),
-    url(r'^circuit-types/add/$', views.CircuitTypeAddView.as_view(), name='circuittype_add'),
+    url(r'^circuit-types/add/$', views.CircuitTypeEditView.as_view(), name='circuittype_add'),
     url(r'^circuit-types/delete/$', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
     url(r'^circuit-types/(?P<slug>[\w-]+)/edit/$', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
 
     # Circuits
     url(r'^circuits/$', views.CircuitListView.as_view(), name='circuit_list'),
-    url(r'^circuits/add/$', views.CircuitAddView.as_view(), name='circuit_add'),
+    url(r'^circuits/add/$', views.CircuitEditView.as_view(), name='circuit_add'),
     url(r'^circuits/import/$', views.CircuitBulkImportView.as_view(), name='circuit_import'),
     url(r'^circuits/edit/$', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
     url(r'^circuits/delete/$', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),

+ 7 - 27
netbox/circuits/views.py

@@ -3,8 +3,8 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
 from django.db.models import Count
 from django.shortcuts import get_object_or_404, render
 
-from utilities.views import BulkImportView, BulkEditView, BulkDeleteView, ObjectListView, ObjectAddView,\
-    ObjectEditView, ObjectDeleteView
+from utilities.views import BulkImportView, BulkEditView, BulkDeleteView, ObjectListView, ObjectEditView,\
+    ObjectDeleteView
 
 from .filters import CircuitFilter
 from .forms import CircuitTypeForm, CircuitTypeBulkDeleteForm, CircuitForm, CircuitImportForm, CircuitBulkEditForm,\
@@ -36,19 +36,12 @@ def provider(request, slug):
     })
 
 
-class ProviderAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'circuits.add_provider'
-    model = Provider
-    form_class = ProviderForm
-    template_name = 'circuits/provider_edit.html'
-    cancel_url = 'circuits:provider_list'
-
-
 class ProviderEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'circuits.change_provider'
     model = Provider
     form_class = ProviderForm
     template_name = 'circuits/provider_edit.html'
+    cancel_url = 'circuits:provider_list'
 
 
 class ProviderDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -101,18 +94,12 @@ class CircuitTypeListView(ObjectListView):
     template_name = 'circuits/circuittype_list.html'
 
 
-class CircuitTypeAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'circuits.add_circuittype'
-    model = CircuitType
-    form_class = CircuitTypeForm
-    cancel_url = 'circuits:circuittype_list'
-
-
 class CircuitTypeEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'circuits.change_circuittype'
     model = CircuitType
     form_class = CircuitTypeForm
-    return_url = 'circuits:circuittype_list'
+    success_url = 'circuits:circuittype_list'
+    cancel_url = 'circuits:circuittype_list'
 
 
 class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
@@ -144,20 +131,13 @@ def circuit(request, pk):
     })
 
 
-class CircuitAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'circuits.add_circuit'
-    model = Circuit
-    form_class = CircuitForm
-    template_name = 'circuits/circuit_edit.html'
-    cancel_url = 'circuits:circuit_list'
-    fields_initial = ['site']
-
-
 class CircuitEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'circuits.change_circuit'
     model = Circuit
     form_class = CircuitForm
+    fields_initial = ['site']
     template_name = 'circuits/circuit_edit.html'
+    cancel_url = 'circuits:circuit_list'
 
 
 class CircuitDeleteView(PermissionRequiredMixin, ObjectDeleteView):

+ 7 - 7
netbox/dcim/urls.py

@@ -11,7 +11,7 @@ urlpatterns = [
 
     # Sites
     url(r'^sites/$', views.SiteListView.as_view(), name='site_list'),
-    url(r'^sites/add/$', views.SiteAddView.as_view(), name='site_add'),
+    url(r'^sites/add/$', views.SiteEditView.as_view(), name='site_add'),
     url(r'^sites/import/$', views.SiteBulkImportView.as_view(), name='site_import'),
     url(r'^sites/(?P<slug>[\w-]+)/$', views.site, name='site'),
     url(r'^sites/(?P<slug>[\w-]+)/edit/$', views.SiteEditView.as_view(), name='site_edit'),
@@ -19,13 +19,13 @@ urlpatterns = [
 
     # Rack groups
     url(r'^rack-groups/$', views.RackGroupListView.as_view(), name='rackgroup_list'),
-    url(r'^rack-groups/add/$', views.RackGroupAddView.as_view(), name='rackgroup_add'),
+    url(r'^rack-groups/add/$', views.RackGroupEditView.as_view(), name='rackgroup_add'),
     url(r'^rack-groups/delete/$', views.RackGroupBulkDeleteView.as_view(), name='rackgroup_bulk_delete'),
     url(r'^rack-groups/(?P<pk>\d+)/edit/$', views.RackGroupEditView.as_view(), name='rackgroup_edit'),
 
     # Racks
     url(r'^racks/$', views.RackListView.as_view(), name='rack_list'),
-    url(r'^racks/add/$', views.RackAddView.as_view(), name='rack_add'),
+    url(r'^racks/add/$', views.RackEditView.as_view(), name='rack_add'),
     url(r'^racks/import/$', views.RackBulkImportView.as_view(), name='rack_import'),
     url(r'^racks/edit/$', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
     url(r'^racks/delete/$', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
@@ -35,13 +35,13 @@ urlpatterns = [
 
     # Manufacturers
     url(r'^manufacturers/$', views.ManufacturerListView.as_view(), name='manufacturer_list'),
-    url(r'^manufacturers/add/$', views.ManufacturerAddView.as_view(), name='manufacturer_add'),
+    url(r'^manufacturers/add/$', views.ManufacturerEditView.as_view(), name='manufacturer_add'),
     url(r'^manufacturers/delete/$', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
     url(r'^manufacturers/(?P<slug>[\w-]+)/edit/$', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
 
     # Device types
     url(r'^device-types/$', views.DeviceTypeListView.as_view(), name='devicetype_list'),
-    url(r'^device-types/add/$', views.DeviceTypeAddView.as_view(), name='devicetype_add'),
+    url(r'^device-types/add/$', views.DeviceTypeEditView.as_view(), name='devicetype_add'),
     url(r'^device-types/edit/$', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
     url(r'^device-types/delete/$', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
     url(r'^device-types/(?P<pk>\d+)/$', views.devicetype, name='devicetype'),
@@ -72,13 +72,13 @@ urlpatterns = [
 
     # Device roles
     url(r'^device-roles/$', views.DeviceRoleListView.as_view(), name='devicerole_list'),
-    url(r'^device-roles/add/$', views.DeviceRoleAddView.as_view(), name='devicerole_add'),
+    url(r'^device-roles/add/$', views.DeviceRoleEditView.as_view(), name='devicerole_add'),
     url(r'^device-roles/delete/$', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
     url(r'^device-roles/(?P<slug>[\w-]+)/edit/$', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
 
     # Devices
     url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'),
-    url(r'^devices/add/$', views.DeviceAddView.as_view(), name='device_add'),
+    url(r'^devices/add/$', views.DeviceEditView.as_view(), name='device_add'),
     url(r'^devices/import/$', views.DeviceBulkImportView.as_view(), name='device_import'),
     url(r'^devices/edit/$', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
     url(r'^devices/delete/$', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),

+ 11 - 58
netbox/dcim/views.py

@@ -17,8 +17,8 @@ from circuits.models import Circuit
 from extras.models import TopologyMap
 from utilities.error_handlers import handle_protectederror
 from utilities.forms import ConfirmationForm
-from utilities.views import ObjectListView, BulkImportView, BulkEditView, BulkDeleteView, ObjectAddView,\
-    ObjectEditView, ObjectDeleteView
+from utilities.views import ObjectListView, BulkImportView, BulkEditView, BulkDeleteView, ObjectEditView,\
+    ObjectDeleteView
 
 from .filters import RackGroupFilter, RackFilter, DeviceTypeFilter, DeviceFilter, ConsoleConnectionFilter,\
     PowerConnectionFilter, InterfaceConnectionFilter
@@ -97,19 +97,12 @@ def site(request, slug):
     })
 
 
-class SiteAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_site'
-    model = Site
-    form_class = SiteForm
-    template_name = 'dcim/site_edit.html'
-    cancel_url = 'dcim:site_list'
-
-
 class SiteEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_site'
     model = Site
     form_class = SiteForm
     template_name = 'dcim/site_edit.html'
+    cancel_url = 'dcim:site_list'
 
 
 class SiteDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -139,18 +132,11 @@ class RackGroupListView(ObjectListView):
     template_name = 'dcim/rackgroup_list.html'
 
 
-class RackGroupAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_rackgroup'
-    model = RackGroup
-    form_class = RackGroupForm
-    cancel_url = 'dcim:rackgroup_list'
-
-
 class RackGroupEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_rackgroup'
     model = RackGroup
     form_class = RackGroupForm
-    return_url = 'dcim:rackgroup_list'
+    cancel_url = 'dcim:rackgroup_list'
 
 
 class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
@@ -191,19 +177,12 @@ def rack(request, pk):
     })
 
 
-class RackAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_rack'
-    model = Rack
-    form_class = RackForm
-    template_name = 'dcim/rack_edit.html'
-    cancel_url = 'dcim:rack_list'
-
-
 class RackEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_rack'
     model = Rack
     form_class = RackForm
     template_name = 'dcim/rack_edit.html'
+    cancel_url = 'dcim:rack_list'
 
 
 class RackDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -256,18 +235,11 @@ class ManufacturerListView(ObjectListView):
     template_name = 'dcim/manufacturer_list.html'
 
 
-class ManufacturerAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_manufacturer'
-    model = Manufacturer
-    form_class = ManufacturerForm
-    cancel_url = 'dcim:manufacturer_list'
-
-
 class ManufacturerEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_manufacturer'
     model = Manufacturer
     form_class = ManufacturerForm
-    return_url = 'dcim:manufacturer_list'
+    cancel_url = 'dcim:manufacturer_list'
 
 
 class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
@@ -317,17 +289,11 @@ def devicetype(request, pk):
     })
 
 
-class DeviceTypeAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_devicetype'
-    model = DeviceType
-    form_class = DeviceTypeForm
-    cancel_url = 'dcim:devicetype_list'
-
-
 class DeviceTypeEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_devicetype'
     model = DeviceType
     form_class = DeviceTypeForm
+    cancel_url = 'dcim:devicetype_list'
 
 
 class DeviceTypeDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -489,18 +455,12 @@ class DeviceRoleListView(ObjectListView):
     template_name = 'dcim/devicerole_list.html'
 
 
-class DeviceRoleAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_devicerole'
-    model = DeviceRole
-    form_class = DeviceRoleForm
-    cancel_url = 'dcim:devicerole_list'
-
-
 class DeviceRoleEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_devicerole'
     model = DeviceRole
     form_class = DeviceRoleForm
-    return_url = 'dcim:devicerole_list'
+    success_url = 'dcim:devicerole_list'
+    cancel_url = 'dcim:devicerole_list'
 
 
 class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
@@ -568,20 +528,13 @@ def device(request, pk):
     })
 
 
-class DeviceAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'dcim.add_device'
-    model = Device
-    form_class = DeviceForm
-    template_name = 'dcim/device_edit.html'
-    cancel_url = 'dcim:device_list'
-    fields_initial = ['site', 'rack', 'position', 'face']
-
-
 class DeviceEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'dcim.change_device'
     model = Device
     form_class = DeviceForm
+    fields_initial = ['site', 'rack', 'position', 'face']
     template_name = 'dcim/device_edit.html'
+    cancel_url = 'dcim:device_list'
 
 
 class DeviceDeleteView(PermissionRequiredMixin, ObjectDeleteView):

+ 5 - 5
netbox/ipam/urls.py

@@ -4,7 +4,7 @@ from . import views
 
 urlpatterns = [
     url(r'^vrfs/$', views.VRFListView.as_view(), name='vrf_list'),
-    url(r'^vrfs/add/$', views.VRFAddView.as_view(), name='vrf_add'),
+    url(r'^vrfs/add/$', views.VRFEditView.as_view(), name='vrf_add'),
     url(r'^vrfs/import/$', views.VRFBulkImportView.as_view(), name='vrf_import'),
     url(r'^vrfs/edit/$', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
     url(r'^vrfs/delete/$', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
@@ -13,7 +13,7 @@ urlpatterns = [
     url(r'^vrfs/(?P<pk>\d+)/delete/$', views.VRFDeleteView.as_view(), name='vrf_delete'),
 
     url(r'^aggregates/$', views.AggregateListView.as_view(), name='aggregate_list'),
-    url(r'^aggregates/add/$', views.AggregateAddView.as_view(), name='aggregate_add'),
+    url(r'^aggregates/add/$', views.AggregateEditView.as_view(), name='aggregate_add'),
     url(r'^aggregates/import/$', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
     url(r'^aggregates/edit/$', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
     url(r'^aggregates/delete/$', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
@@ -22,7 +22,7 @@ urlpatterns = [
     url(r'^aggregates/(?P<pk>\d+)/delete/$', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
 
     url(r'^prefixes/$', views.PrefixListView.as_view(), name='prefix_list'),
-    url(r'^prefixes/add/$', views.PrefixAddView.as_view(), name='prefix_add'),
+    url(r'^prefixes/add/$', views.PrefixEditView.as_view(), name='prefix_add'),
     url(r'^prefixes/import/$', views.PrefixBulkImportView.as_view(), name='prefix_import'),
     url(r'^prefixes/edit/$', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
     url(r'^prefixes/delete/$', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
@@ -32,7 +32,7 @@ urlpatterns = [
     url(r'^prefixes/(?P<pk>\d+)/ip-addresses/$', views.prefix_ipaddresses, name='prefix_ipaddresses'),
 
     url(r'^ip-addresses/$', views.IPAddressListView.as_view(), name='ipaddress_list'),
-    url(r'^ip-addresses/add/$', views.IPAddressAddView.as_view(), name='ipaddress_add'),
+    url(r'^ip-addresses/add/$', views.IPAddressEditView.as_view(), name='ipaddress_add'),
     url(r'^ip-addresses/import/$', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
     url(r'^ip-addresses/edit/$', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
     url(r'^ip-addresses/delete/$', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
@@ -41,7 +41,7 @@ urlpatterns = [
     url(r'^ip-addresses/(?P<pk>\d+)/delete/$', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
 
     url(r'^vlans/$', views.VLANListView.as_view(), name='vlan_list'),
-    url(r'^vlans/add/$', views.VLANAddView.as_view(), name='vlan_add'),
+    url(r'^vlans/add/$', views.VLANEditView.as_view(), name='vlan_add'),
     url(r'^vlans/import/$', views.VLANBulkImportView.as_view(), name='vlan_import'),
     url(r'^vlans/edit/$', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
     url(r'^vlans/delete/$', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),

+ 9 - 40
netbox/ipam/views.py

@@ -8,8 +8,8 @@ from django.shortcuts import get_object_or_404, render
 
 from dcim.models import Device
 from utilities.paginator import EnhancedPaginator
-from utilities.views import BulkImportView, BulkEditView, BulkDeleteView, ObjectListView, ObjectAddView,\
-    ObjectEditView, ObjectDeleteView
+from utilities.views import BulkImportView, BulkEditView, BulkDeleteView, ObjectListView, ObjectEditView,\
+    ObjectDeleteView
 
 from .filters import AggregateFilter, PrefixFilter, IPAddressFilter, VLANFilter, VRFFilter
 from .forms import AggregateForm, AggregateImportForm, AggregateBulkEditForm, AggregateBulkDeleteForm,\
@@ -61,17 +61,11 @@ def vrf(request, pk):
     })
 
 
-class VRFAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'ipam.add_vrf'
-    model = VRF
-    form_class = VRFForm
-    cancel_url = 'ipam:vrf_list'
-
-
 class VRFEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_vrf'
     model = VRF
     form_class = VRFForm
+    cancel_url = 'ipam:vrf_list'
 
 
 class VRFDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -149,17 +143,11 @@ def aggregate(request, pk):
     })
 
 
-class AggregateAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'ipam.add_aggregate'
-    model = Aggregate
-    form_class = AggregateForm
-    cancel_url = 'ipam:aggregate_list'
-
-
 class AggregateEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_aggregate'
     model = Aggregate
     form_class = AggregateForm
+    cancel_url = 'ipam:aggregate_list'
 
 
 class AggregateDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -262,18 +250,12 @@ def prefix(request, pk):
     })
 
 
-class PrefixAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'ipam.add_prefix'
-    model = Prefix
-    form_class = PrefixForm
-    cancel_url = 'ipam:prefix_list'
-    fields_initial = ['site', 'vrf', 'prefix']
-
-
 class PrefixEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_prefix'
     model = Prefix
     form_class = PrefixForm
+    fields_initial = ['site', 'vrf', 'prefix']
+    cancel_url = 'ipam:prefix_list'
 
 
 class PrefixDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -369,20 +351,13 @@ def ipaddress(request, pk):
     })
 
 
-class IPAddressAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'ipam.add_ipaddress'
-    model = IPAddress
-    form_class = IPAddressForm
-    template_name = 'ipam/ipaddress_edit.html'
-    cancel_url = 'ipam:ipaddress_list'
-    fields_initial = ['ipaddress']
-
-
 class IPAddressEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_ipaddress'
     model = IPAddress
     form_class = IPAddressForm
+    fields_initial = ['ipaddress']
     template_name = 'ipam/ipaddress_edit.html'
+    cancel_url = 'ipam:ipaddress_list'
 
 
 class IPAddressDeleteView(PermissionRequiredMixin, ObjectDeleteView):
@@ -462,17 +437,11 @@ def vlan(request, pk):
     })
 
 
-class VLANAddView(PermissionRequiredMixin, ObjectAddView):
-    permission_required = 'ipam.add_vlan'
-    model = VLAN
-    form_class = VLANForm
-    cancel_url = 'ipam:vlan_list'
-
-
 class VLANEditView(PermissionRequiredMixin, ObjectEditView):
     permission_required = 'ipam.change_vlan'
     model = VLAN
     form_class = VLANForm
+    cancel_url = 'ipam:vlan_list'
 
 
 class VLANDeleteView(PermissionRequiredMixin, ObjectDeleteView):

+ 25 - 50
netbox/utilities/views.py

@@ -75,48 +75,13 @@ class ObjectListView(View):
         return self.queryset
 
 
-class ObjectAddView(View):
-    model = None
-    form_class = None
-    template_name = 'utilities/obj_edit.html'
-    cancel_url = None
-    fields_initial = []
-
-    def get(self, request):
-
-        initial = {k: request.GET.get(k) for k in self.fields_initial}
-        form = self.form_class(initial=initial)
-
-        return render(request, self.template_name, {
-            'form': form,
-            'obj_type': self.model._meta.verbose_name,
-            'cancel_url': reverse(self.cancel_url),
-        })
-
-    def post(self, request):
-
-        form = self.form_class(request.POST)
-        if form.is_valid():
-            obj = form.save()
-            messages.success(request, 'Added new {} <a href="{}">{}</a>'.format(self.model._meta.verbose_name,
-                                                                                obj.get_absolute_url(), obj))
-            if '_addanother' in request.POST:
-                return redirect(request.path)
-            else:
-                return redirect(obj.get_absolute_url())
-
-        return render(request, self.template_name, {
-            'form': form,
-            'obj_type': self.model._meta.verbose_name,
-            'cancel_url': reverse(self.cancel_url),
-        })
-
-
 class ObjectEditView(View):
     model = None
     form_class = None
+    fields_initial = []
     template_name = 'utilities/obj_edit.html'
-    return_url = None
+    success_url = None
+    cancel_url = None
 
     def get_object(self, kwargs):
         # Look up object by slug if one has been provided. Otherwise, use PK.
@@ -127,36 +92,46 @@ class ObjectEditView(View):
 
     def get(self, request, *args, **kwargs):
 
-        obj = self.get_object(kwargs)
-        form = self.form_class(instance=obj)
+        if kwargs:
+            obj = self.get_object(kwargs)
+            form = self.form_class(instance=obj)
+        else:
+            obj = None
+            form = self.form_class(initial={k: request.GET.get(k) for k in self.fields_initial})
 
         return render(request, self.template_name, {
             'obj': obj,
-            'form': form,
             'obj_type': self.model._meta.verbose_name,
-            'cancel_url': reverse(self.return_url) if self.return_url else obj.get_absolute_url(),
+            'form': form,
+            'cancel_url': reverse(self.cancel_url) if self.cancel_url else obj.get_absolute_url(),
         })
 
     def post(self, request, *args, **kwargs):
 
-        obj = self.get_object(kwargs)
+        # Validate object if editing an existing object
+        obj = self.get_object(kwargs) if kwargs else None
+
         form = self.form_class(request.POST, instance=obj)
         if form.is_valid():
-            obj = form.save()
-            messages.success(request, 'Modified {} <a href="{}">{}</a>'.format(self.model._meta.verbose_name,
-                                                                               obj.get_absolute_url(), obj))
+            obj = form.save(commit=False)
+            obj_created = not obj.pk
+            obj.save()
+            messages.success(request, '{} {} <a href="{}">{}</a>'.format('Created' if obj_created else 'Modified',
+                                                                         self.model._meta.verbose_name,
+                                                                         obj.get_absolute_url(),
+                                                                         obj))
             if '_addanother' in request.POST:
                 return redirect(request.path)
-            elif self.return_url:
-                return redirect(self.return_url)
+            elif self.success_url:
+                return redirect(self.success_url)
             else:
                 return redirect(obj.get_absolute_url())
 
         return render(request, self.template_name, {
             'obj': obj,
-            'form': form,
             'obj_type': self.model._meta.verbose_name,
-            'cancel_url': reverse(self.return_url) if self.return_url else obj.get_absolute_url(),
+            'form': form,
+            'cancel_url': reverse(self.cancel_url) if self.cancel_url else obj.get_absolute_url(),
         })