Reopened treechanges. Moved validation language to validators.py. Abstracted NodeForm...
authormelinath <stephen.r.burrows@gmail.com>
Fri, 11 Jun 2010 17:02:08 +0000 (13:02 -0400)
committermelinath <stephen.r.burrows@gmail.com>
Fri, 11 Jun 2010 17:02:08 +0000 (13:02 -0400)
admin.py
models.py
validators.py [new file with mode: 0644]

index f63b576..af97bde 100644 (file)
--- a/admin.py
+++ b/admin.py
@@ -9,6 +9,7 @@ from django.utils.html import escape
 from django.utils.text import truncate_words
 from models import *
 from django.core.exceptions import ValidationError, ObjectDoesNotExist
 from django.utils.text import truncate_words
 from models import *
 from django.core.exceptions import ValidationError, ObjectDoesNotExist
+from validators import TreeParentValidator, TreePositionValidator
 
 
 class AttributeInline(generic.GenericTabularInline):
 
 
 class AttributeInline(generic.GenericTabularInline):
@@ -46,6 +47,38 @@ class CollectionAdmin(admin.ModelAdmin):
        inlines = [CollectionMemberInline]
 
 
        inlines = [CollectionMemberInline]
 
 
+class TreeForm(forms.ModelForm):
+       def __init__(self, *args, **kwargs):
+               super(TreeForm, self).__init__(*args, **kwargs)
+               instance = self.instance
+               instance_class=self.get_instance_class()
+               try:
+                       self.fields['parent'].queryset = instance_class.objects.exclude(id=instance.id)
+               except ObjectDoesNotExist:
+                       pass
+               self.fields['parent'].validators = [TreeParentValidator(*self.get_validator_args())]
+       
+       def get_instance_class(self):
+               return self.instance.__class__
+               
+       def get_validator_args(self):
+               return [self.instance]
+       
+       def clean(self):
+               cleaned_data = self.cleaned_data
+               
+               try:
+                       parent = cleaned_data['parent']
+                       slug = cleaned_data['slug']
+                       obj_class = self.get_instance_class()
+                       tpv = TreePositionValidator(parent, slug, obj_class)
+                       tpv(self.instance)
+               except KeyError:
+                       pass
+               
+               return cleaned_data
+
+
 class TemplateAdmin(admin.ModelAdmin):
        prepopulated_fields = {'slug': ('name',)}
        fieldsets = (
 class TemplateAdmin(admin.ModelAdmin):
        prepopulated_fields = {'slug': ('name',)}
        fieldsets = (
@@ -67,6 +100,7 @@ class TemplateAdmin(admin.ModelAdmin):
        save_on_top = True
        save_as = True
        list_display = ('__unicode__', 'slug', 'get_path',)
        save_on_top = True
        save_as = True
        list_display = ('__unicode__', 'slug', 'get_path',)
+       form = TreeForm
 
 
 class ModelLookupWidget(forms.TextInput):
 
 
 class ModelLookupWidget(forms.TextInput):
@@ -95,41 +129,30 @@ 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
+
+class NodeForm(TreeForm):
+       def get_instance_class(self):
+               return self.instance.node_ptr.__class__
                
                
-               return cleaned_data
+       def get_validator_args(self):
+               return [self.instance, 'instance']
+
 
 class PageAdminForm(NodeForm):
        class Meta:
                model=Page
 
 
 class PageAdminForm(NodeForm):
        class Meta:
                model=Page
 
+
 class RedirectAdminForm(NodeForm):
        class Meta:
                model=Redirect
 class RedirectAdminForm(NodeForm):
        class Meta:
                model=Redirect
-               
+
+
 class FileAdminForm(NodeForm):
        class Meta:
                model=File
 
 class FileAdminForm(NodeForm):
        class Meta:
                model=File
 
+
 class PageAdmin(EntityAdmin):
        prepopulated_fields = {'slug': ('title',)}
        fieldsets = (
 class PageAdmin(EntityAdmin):
        prepopulated_fields = {'slug': ('title',)}
        fieldsets = (
@@ -209,15 +232,18 @@ class PageAdmin(EntityAdmin):
                                        contentreference.content = content
                                        contentreference.save()
 
                                        contentreference.content = content
                                        contentreference.save()
 
+
 class RedirectAdmin(admin.ModelAdmin):
        list_display=('slug', 'target', 'path', 'status_code',)
        list_filter=('status_code',)
        form = RedirectAdminForm
 
 class RedirectAdmin(admin.ModelAdmin):
        list_display=('slug', 'target', 'path', 'status_code',)
        list_filter=('status_code',)
        form = RedirectAdminForm
 
+
 class FileAdmin(admin.ModelAdmin):
        form=FileAdminForm
        list_display=('slug', 'mimetype', 'path', 'file',)
 
 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(File, FileAdmin)
 admin.site.register(Collection, CollectionAdmin)
 admin.site.register(Redirect, RedirectAdmin)
 admin.site.register(File, FileAdmin)
index 878d3d4..d3bf5d4 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, ValidationError
+from django.core.exceptions import ObjectDoesNotExist
 try:
        import json
 except ImportError:
 try:
        import json
 except ImportError:
@@ -157,6 +157,7 @@ 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)
@@ -166,7 +167,6 @@ 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,30 +177,6 @@ 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):
@@ -231,10 +207,6 @@ 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, '?')
diff --git a/validators.py b/validators.py
new file mode 100644 (file)
index 0000000..bc41d02
--- /dev/null
@@ -0,0 +1,77 @@
+from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+
+
+class TreeParentValidator(object):
+       """
+       constructor takes instance and parent_attr, where instance is the model
+       being validated and parent_attr is where to look on that parent for the
+       comparison.
+       """
+       #message = _("A tree element can't be its own parent.")
+       code = 'invalid'
+       
+       def __init__(self, instance, parent_attr=None, message=None, code=None):
+               self.instance = instance
+               self.parent_attr = parent_attr
+               self.static_message = message
+               if code is not None:
+                       self.code = code
+       
+       def __call__(self, value):
+               """
+               Validates that the self.instance is not found in the parent tree of
+               the node given as value.
+               """
+               parent = value
+               
+               while parent:
+                       comparison=self.get_comparison(parent)
+                       if comparison == self.instance:
+                               # using (self.message, code=self.code) results in the admin interface
+                               # screwing with the error message and making it be 'Enter a valid value'
+                               raise ValidationError(self.message)
+                       parent=parent.parent
+       
+       def get_comparison(self, parent):
+               if self.parent_attr and hasattr(parent, self.parent_attr):
+                       return getattr(parent, self.parent_attr)
+               
+               return parent
+       
+       def get_message(self):
+               return self.static_message or _(u"A %s can't be its own parent." % self.instance.__class__.__name__)
+       message = property(get_message)
+       
+class TreePositionValidator(object):
+       code = 'invalid'
+       
+       def __init__(self, parent, slug, obj_class, message=None, code=None):
+               self.parent = parent
+               self.slug = slug
+               self.obj_class = obj_class
+               self.static_message = message
+                       
+               if code is not None:
+                       self.code = code
+       
+       def __call__(self, value):
+               """
+               Validates that there is no obj of obj_class with the same position
+               as the compared obj (value) but a different id.
+               """
+               if not isinstance(value, self.obj_class):
+                       raise ValidationError(_(u"The value must be an instance of %s." % self.obj_class.__name__))
+               
+               try:
+                       obj = self.obj_class.objects.get(slug=self.slug, parent=self.parent)
+                       
+                       if obj.id != value.id:
+                               raise ValidationError(self.message)
+                               
+               except self.obj_class.DoesNotExist:
+                       pass
+       
+       def get_message(self):
+               return self.static_message or _(u"A %s with that path (parent and slug) already exists." % self.obj_class.__name__)
+       message = property(get_message)