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):
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 = (
save_on_top = True
save_as = True
list_display = ('__unicode__', 'slug', 'get_path',)
+ form = TreeForm
class ModelLookupWidget(forms.TextInput):
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 RedirectAdminForm(NodeForm):
class Meta:
model=Redirect
-
+
+
class FileAdminForm(NodeForm):
class Meta:
model=File
+
class PageAdmin(EntityAdmin):
prepopulated_fields = {'slug': ('title',)}
fieldsets = (
contentreference.content = content
contentreference.save()
+
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',)
+
admin.site.register(Collection, CollectionAdmin)
admin.site.register(Redirect, RedirectAdmin)
admin.site.register(File, FileAdmin)
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:
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)
path = getattr(self, field, '?')
parent = self.parent
while parent:
- self.validate_parent(parent)
path = getattr(parent, field, '?') + pathsep + path
parent = parent.parent
return path
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):
@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, '?')
--- /dev/null
+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)