Added admin form validation to prevent:
authormelinath <stephen.r.burrows@gmail.com>
Thu, 10 Jun 2010 22:02:52 +0000 (18:02 -0400)
committermelinath <stephen.r.burrows@gmail.com>
Mon, 14 Jun 2010 19:38:32 +0000 (15:38 -0400)
1. a treemodel being its own parent (uses a validate_parents method added to the model.)
2. a node having the same path (i.e. parent+slug) as another
Also added a ModelAdmin for the File model.

admin.py
models.py

index 73dea0a..f63b576 100644 (file)
--- a/admin.py
+++ b/admin.py
@@ -8,6 +8,7 @@ from django.utils.safestring import mark_safe
 from django.utils.html import escape
 from django.utils.text import truncate_words
 from models import *
 from django.utils.html import escape
 from django.utils.text import truncate_words
 from models import *
+from django.core.exceptions import ValidationError, ObjectDoesNotExist
 
 
 class AttributeInline(generic.GenericTabularInline):
 
 
 class AttributeInline(generic.GenericTabularInline):
@@ -94,6 +95,40 @@ class ModelLookupWidget(forms.TextInput):
                                pass
                return mark_safe(output)
 
                                pass
                return mark_safe(output)
 
+class NodeForm(forms.ModelForm):
+       
+       def __init__(self, *args, **kwargs):
+               super(NodeForm, self).__init__(*args, **kwargs)
+               instance = self.instance
+               try:
+                       self.fields['parent'].queryset = instance.node_ptr.__class__.objects.exclude(id=instance.id)
+               except ObjectDoesNotExist:
+                       pass
+       
+       def clean(self):
+               cleaned_data = self.cleaned_data
+               parent = self.cleaned_data['parent']
+               self.instance.validate_parents(parent)
+                       
+               try:
+                       Node.objects.get(slug=self.cleaned_data['slug'], parent=parent)
+                       raise ValidationError("A node with that path (parent and slug) already exists.")
+               except ObjectDoesNotExist:
+                       pass
+               
+               return cleaned_data
+
+class PageAdminForm(NodeForm):
+       class Meta:
+               model=Page
+
+class RedirectAdminForm(NodeForm):
+       class Meta:
+               model=Redirect
+               
+class FileAdminForm(NodeForm):
+       class Meta:
+               model=File
 
 class PageAdmin(EntityAdmin):
        prepopulated_fields = {'slug': ('title',)}
 
 class PageAdmin(EntityAdmin):
        prepopulated_fields = {'slug': ('title',)}
@@ -109,6 +144,7 @@ class PageAdmin(EntityAdmin):
        list_display = ('title', 'path', 'template')
        list_filter = ('template',)
        search_fields = ['title', 'slug', 'contentlets__content']
        list_display = ('title', 'path', 'template')
        list_filter = ('template',)
        search_fields = ['title', 'slug', 'contentlets__content']
+       form = PageAdminForm
        
        def get_fieldsets(self, request, obj=None, **kwargs):
                fieldsets = list(self.fieldsets)
        
        def get_fieldsets(self, request, obj=None, **kwargs):
                fieldsets = list(self.fieldsets)
@@ -174,11 +210,16 @@ class PageAdmin(EntityAdmin):
                                        contentreference.save()
 
 class RedirectAdmin(admin.ModelAdmin):
                                        contentreference.save()
 
 class RedirectAdmin(admin.ModelAdmin):
-       list_display=('slug', 'target', 'status_code',)
+       list_display=('slug', 'target', 'path', 'status_code',)
        list_filter=('status_code',)
        list_filter=('status_code',)
+       form = RedirectAdminForm
+
+class FileAdmin(admin.ModelAdmin):
+       form=FileAdminForm
+       list_display=('slug', 'mimetype', 'path', 'file',)
 
 admin.site.register(Collection, CollectionAdmin)
 admin.site.register(Redirect, RedirectAdmin)
 
 admin.site.register(Collection, CollectionAdmin)
 admin.site.register(Redirect, RedirectAdmin)
-admin.site.register(File)
+admin.site.register(File, FileAdmin)
 admin.site.register(Page, PageAdmin)
 admin.site.register(Template, TemplateAdmin)
 admin.site.register(Page, PageAdmin)
 admin.site.register(Template, TemplateAdmin)
index 6eac9de..878d3d4 100644 (file)
--- a/models.py
+++ b/models.py
@@ -10,7 +10,7 @@ from django.template import add_to_builtins as register_templatetags
 from django.template import Template as DjangoTemplate
 from django.template import TemplateDoesNotExist
 from django.template import Context, RequestContext
 from django.template import Template as DjangoTemplate
 from django.template import TemplateDoesNotExist
 from django.template import Context, RequestContext
-from django.core.exceptions import ObjectDoesNotExist
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
 try:
        import json
 except ImportError:
 try:
        import json
 except ImportError:
@@ -157,7 +157,6 @@ class TreeManager(models.Manager):
                                return (obj, remainder)
                raise self.model.DoesNotExist('%s matching query does not exist.' % self.model._meta.object_name)
 
                                return (obj, remainder)
                raise self.model.DoesNotExist('%s matching query does not exist.' % self.model._meta.object_name)
 
-
 class TreeModel(models.Model):
        objects = TreeManager()
        parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
 class TreeModel(models.Model):
        objects = TreeManager()
        parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
@@ -167,6 +166,7 @@ class TreeModel(models.Model):
                path = getattr(self, field, '?')
                parent = self.parent
                while parent:
                path = getattr(self, field, '?')
                parent = self.parent
                while parent:
+                       self.validate_parent(parent)
                        path = getattr(parent, field, '?') + pathsep + path
                        parent = parent.parent
                return path
                        path = getattr(parent, field, '?') + pathsep + path
                        parent = parent.parent
                return path
@@ -177,6 +177,30 @@ class TreeModel(models.Model):
        
        class Meta:
                abstract = True
        
        class Meta:
                abstract = True
+               
+       def validate_parents(self, parent=None):
+               if parent == None:
+                       parent = self.parent
+               
+               while parent:
+                       try:
+                               self.validate_parent(parent)
+                               parent = parent.parent
+                       except ObjectDoesNotExist:
+                               return # because it likely means the child doesn't exist 
+                       
+       def validate_parent(self, parent):
+               #Why doesn't this stop the Admin site from saving a model with itself as parent?
+               if self == parent:
+                       raise ValidationError("A %s can't be its own parent." % self.__class__.__name__)
+       
+       def clean(self):
+               super(TreeModel, self).clean()
+               self.validate_parents()
+       
+       def save(self, *args, **kwargs):
+               self.clean()
+               super(TreeModel, self).save(*args, **kwargs)
 
 
 class TreeEntity(TreeModel, Entity):
 
 
 class TreeEntity(TreeModel, Entity):
@@ -207,6 +231,10 @@ class InheritableTreeEntity(TreeEntity):
        @property
        def instance(self):
                return self.instance_type.get_object_for_this_type(id=self.id)
        @property
        def instance(self):
                return self.instance_type.get_object_for_this_type(id=self.id)
+               
+       def validate_parent(self, parent):
+               if self.instance == parent.instance:
+                       raise ValidationError("A %s can't be its own parent." % self.__class__.__name__)
        
        def get_path(self, pathsep='/', field='slug'):
                path = getattr(self.instance, field, '?')
        
        def get_path(self, pathsep='/', field='slug'):
                path = getattr(self.instance, field, '?')
@@ -238,6 +266,9 @@ class Node(InheritableTreeEntity):
        
        def render_to_response(self, request, path=None, subpath=None):
                return HttpResponseServerError()
        
        def render_to_response(self, request, path=None, subpath=None):
                return HttpResponseServerError()
+               
+       class Meta:
+               unique_together=(('parent', 'slug',),)
 
 
 class MultiNode(Node):
 
 
 class MultiNode(Node):