From 4d6de7cca540e95b4355cfbc14d068ee2e5b577b Mon Sep 17 00:00:00 2001 From: melinath Date: Thu, 10 Jun 2010 18:02:52 -0400 Subject: [PATCH] Added admin form validation to prevent: 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 | 45 +++++++++++++++++++++++++++++++++++++++++++-- models.py | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/admin.py b/admin.py index 73dea0a..f63b576 100644 --- 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.core.exceptions import ValidationError, ObjectDoesNotExist class AttributeInline(generic.GenericTabularInline): @@ -94,6 +95,40 @@ 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 + + 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',)} @@ -109,6 +144,7 @@ class PageAdmin(EntityAdmin): 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) @@ -174,11 +210,16 @@ class PageAdmin(EntityAdmin): contentreference.save() class RedirectAdmin(admin.ModelAdmin): - list_display=('slug', 'target', 'status_code',) + 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) +admin.site.register(File, FileAdmin) admin.site.register(Page, PageAdmin) admin.site.register(Template, TemplateAdmin) diff --git a/models.py b/models.py index 6eac9de..878d3d4 100644 --- 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.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, ValidationError 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) - 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: + self.validate_parent(parent) path = getattr(parent, field, '?') + pathsep + path parent = parent.parent return path @@ -177,6 +177,30 @@ class TreeModel(models.Model): 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): @@ -207,6 +231,10 @@ class InheritableTreeEntity(TreeEntity): @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, '?') @@ -238,6 +266,9 @@ class Node(InheritableTreeEntity): def render_to_response(self, request, path=None, subpath=None): return HttpResponseServerError() + + class Meta: + unique_together=(('parent', 'slug',),) class MultiNode(Node): -- 2.20.1