Commit 322b5a6a by benjaoming

Finalizing URLPath as an MPTT model and generic relations on Articles

parent 83fe3d46
from django.contrib import admin
from django.contrib.contenttypes.generic import GenericTabularInline
from mptt.admin import MPTTModelAdmin
import models
class ArticleObjectAdmin(GenericTabularInline):
model = models.ArticleForObject
extra = 1
max_num = 1
class ArticleAdmin(admin.ModelAdmin):
pass
class URLPathAdmin(MPTTModelAdmin):
inlines = [ArticleObjectAdmin]
list_filter = ('site',)
list_display = ('slug', 'article')
admin.site.register(models.URLPath, URLPathAdmin)
admin.site.register(models.Article, ArticleAdmin)
\ No newline at end of file
...@@ -13,17 +13,27 @@ class Migration(SchemaMigration): ...@@ -13,17 +13,27 @@ class Migration(SchemaMigration):
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('title', self.gf('django.db.models.fields.CharField')(max_length=512)), ('title', self.gf('django.db.models.fields.CharField')(max_length=512)),
('current_revision', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='current_set', null=True, to=orm['wiki.ArticleRevision'])), ('current_revision', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='current_set', null=True, to=orm['wiki.ArticleRevision'])),
('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.Group'], null=True, blank=True)),
('group_read', self.gf('django.db.models.fields.BooleanField')(default=True)),
('group_write', self.gf('django.db.models.fields.BooleanField')(default=True)),
('other_read', self.gf('django.db.models.fields.BooleanField')(default=True)),
('other_write', self.gf('django.db.models.fields.BooleanField')(default=True)),
)) ))
db.send_create_signal('wiki', ['Article']) db.send_create_signal('wiki', ['Article'])
# Adding model 'ObjectForArticle' # Adding model 'ArticleForObject'
db.create_table('wiki_objectforarticle', ( db.create_table('wiki_articleforobject', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])), ('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])),
('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='content_type_set_for_objectforarticle', to=orm['contenttypes.ContentType'])), ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='content_type_set_for_articleforobject', to=orm['contenttypes.ContentType'])),
('object_pk', self.gf('django.db.models.fields.TextField')()), ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
('has_parent_method', self.gf('django.db.models.fields.BooleanField')(default=False)),
)) ))
db.send_create_signal('wiki', ['ObjectForArticle']) db.send_create_signal('wiki', ['ArticleForObject'])
# Adding unique constraint on 'ArticleForObject', fields ['content_type', 'object_id']
db.create_unique('wiki_articleforobject', ['content_type_id', 'object_id'])
# Adding model 'ArticleRevision' # Adding model 'ArticleRevision'
db.create_table('wiki_articlerevision', ( db.create_table('wiki_articlerevision', (
...@@ -35,14 +45,15 @@ class Migration(SchemaMigration): ...@@ -35,14 +45,15 @@ class Migration(SchemaMigration):
('redirect', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='redirect_set', null=True, to=orm['wiki.Article'])), ('redirect', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='redirect_set', null=True, to=orm['wiki.Article'])),
('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True)), ('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
)) ))
db.send_create_signal('wiki', ['ArticleRevision']) db.send_create_signal('wiki', ['ArticleRevision'])
# Adding model 'URLPath' # Adding model 'URLPath'
db.create_table('wiki_urlpath', ( db.create_table('wiki_urlpath', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('article', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Article'])), ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, null=True, blank=True)),
('slug', self.gf('django.db.models.fields.SlugField')(max_length=50)),
('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])), ('site', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sites.Site'])),
('parent', self.gf('mptt.fields.TreeForeignKey')(blank=True, related_name='children', null=True, to=orm['wiki.URLPath'])), ('parent', self.gf('mptt.fields.TreeForeignKey')(blank=True, related_name='children', null=True, to=orm['wiki.URLPath'])),
('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), ('lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)),
...@@ -59,11 +70,14 @@ class Migration(SchemaMigration): ...@@ -59,11 +70,14 @@ class Migration(SchemaMigration):
# Removing unique constraint on 'URLPath', fields ['site', 'parent', 'slug'] # Removing unique constraint on 'URLPath', fields ['site', 'parent', 'slug']
db.delete_unique('wiki_urlpath', ['site_id', 'parent_id', 'slug']) db.delete_unique('wiki_urlpath', ['site_id', 'parent_id', 'slug'])
# Removing unique constraint on 'ArticleForObject', fields ['content_type', 'object_id']
db.delete_unique('wiki_articleforobject', ['content_type_id', 'object_id'])
# Deleting model 'Article' # Deleting model 'Article'
db.delete_table('wiki_article') db.delete_table('wiki_article')
# Deleting model 'ObjectForArticle' # Deleting model 'ArticleForObject'
db.delete_table('wiki_objectforarticle') db.delete_table('wiki_articleforobject')
# Deleting model 'ArticleRevision' # Deleting model 'ArticleRevision'
db.delete_table('wiki_articlerevision') db.delete_table('wiki_articlerevision')
...@@ -117,37 +131,45 @@ class Migration(SchemaMigration): ...@@ -117,37 +131,45 @@ class Migration(SchemaMigration):
'wiki.article': { 'wiki.article': {
'Meta': {'object_name': 'Article'}, 'Meta': {'object_name': 'Article'},
'current_revision': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_set'", 'null': 'True', 'to': "orm['wiki.ArticleRevision']"}), 'current_revision': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'current_set'", 'null': 'True', 'to': "orm['wiki.ArticleRevision']"}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}) 'title': ('django.db.models.fields.CharField', [], {'max_length': '512'})
}, },
'wiki.articleforobject': {
'Meta': {'unique_together': "(('content_type', 'object_id'),)", 'object_name': 'ArticleForObject'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_articleforobject'", 'to': "orm['contenttypes.ContentType']"}),
'has_parent_method': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
},
'wiki.articlerevision': { 'wiki.articlerevision': {
'Meta': {'object_name': 'ArticleRevision'}, 'Meta': {'object_name': 'ArticleRevision'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}), 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}), 'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'redirect': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'redirect_set'", 'null': 'True', 'to': "orm['wiki.Article']"}), 'redirect': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'redirect_set'", 'null': 'True', 'to': "orm['wiki.Article']"}),
'revision_number': ('django.db.models.fields.IntegerField', [], {}), 'revision_number': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
}, },
'wiki.objectforarticle': {
'Meta': {'object_name': 'ObjectForArticle'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_objectforarticle'", 'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_pk': ('django.db.models.fields.TextField', [], {})
},
'wiki.urlpath': { 'wiki.urlpath': {
'Meta': {'unique_together': "(('site', 'parent', 'slug'),)", 'object_name': 'URLPath'}, 'Meta': {'unique_together': "(('site', 'parent', 'slug'),)", 'object_name': 'URLPath'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['wiki.URLPath']"}), 'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['wiki.URLPath']"}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}), 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
} }
} }
......
...@@ -4,7 +4,7 @@ from django.conf import settings as django_settings ...@@ -4,7 +4,7 @@ from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
import warnings import warnings
from article import Article, ArticleRevision, ObjectForArticle from article import Article, ArticleRevision, ArticleForObject
from urlpath import URLPath from urlpath import URLPath
###################### ######################
......
...@@ -15,15 +15,6 @@ class Article(models.Model): ...@@ -15,15 +15,6 @@ class Article(models.Model):
verbose_name=_(u'current revision'), verbose_name=_(u'current revision'),
blank=True, null=True, related_name='current_set') blank=True, null=True, related_name='current_set')
# Permissions. If nothing is set, the article will inherit from
# some other parent, whatever the semantics dictate. For instance, using
# URLPaths means that the article inherits from its URLPath parent.
# Inheriting permissions requires a "get_parent_articles" method to exist on
# one of the objects related to the article.
# TIP: The related object with a get_parent_articles method should be an
# MPTTModel inheritor for efficiency. See the URLPath model.
owner = models.ForeignKey(User, verbose_name=_('owner'), owner = models.ForeignKey(User, verbose_name=_('owner'),
blank=True, null=True) blank=True, null=True)
...@@ -36,10 +27,53 @@ class Article(models.Model): ...@@ -36,10 +27,53 @@ class Article(models.Model):
other_write = models.BooleanField(default=True) other_write = models.BooleanField(default=True)
def can_read(self, user=None, group=None): def can_read(self, user=None, group=None):
return True if self.other_read:
return True
if user == self.owner:
return True
if self.group_read:
if group == self.group:
return True
if self.group and user and user.groups.filter(group=group):
return True
return False
def can_write(self, user=None, group=None): def can_write(self, user=None, group=None):
return True if self.other_write:
return True
if user == self.owner:
return True
if self.group_write:
if group == self.group:
return True
if self.group and user and user.groups.filter(group=group):
return True
return False
def decendant_objects(self):
for obj in self.objectforarticle_set.filter(has_parent_field=True):
for decendant in obj.get_decendants():
yield decendant
# All recursive permission methods will use decendant_objects to access
# generic relations and check if they are using MPTT and have INHERIT_PERMISSIONS=True
def set_permissions_recursive(self):
for decendant in self.decendant_objects():
if decendant.INHERIT_PERMISSIONS:
decendant.group_read = self.group_read
decendant.group_write = self.group_write
decendant.other_read = self.other_read
decendant.other_write = self.other_write
def set_group_recursive(self):
for decendant in self.decendant_objects():
if decendant.INHERIT_PERMISSIONS:
decendant.group = self.group
def set_owner_recursive(self):
for decendant in self.decendant_objects():
if decendant.INHERIT_PERMISSIONS:
decendant.owner = self.owner
def add_revision(self, new_revision, save=True): def add_revision(self, new_revision, save=True):
""" """
...@@ -49,6 +83,7 @@ class Article(models.Model): ...@@ -49,6 +83,7 @@ class Article(models.Model):
assert self.id or save, ('Article.add_revision: Sorry, you cannot add a' assert self.id or save, ('Article.add_revision: Sorry, you cannot add a'
'revision to an article that has not been saved ' 'revision to an article that has not been saved '
'without using save=True') 'without using save=True')
if not self.id: self.save()
revisions = self.articlerevision_set.all() revisions = self.articlerevision_set.all()
try: try:
new_revision.revision_number = revisions.latest().revision_number + 1 new_revision.revision_number = revisions.latest().revision_number + 1
...@@ -61,25 +96,40 @@ class Article(models.Model): ...@@ -61,25 +96,40 @@ class Article(models.Model):
def add_object_relation(self, obj): def add_object_relation(self, obj):
content_type = ContentType.objects.get_for_model(obj) content_type = ContentType.objects.get_for_model(obj)
rel = ObjectForArticle.objects.get_or_create(article=self, has_parent_field = hasattr(obj, 'parent')
rel = ArticleForObject.objects.get_or_create(article=self,
content_type=content_type, content_type=content_type,
object_pk=obj.pk,) object_pk=obj.pk,
has_parent_method=has_parent_field)
return rel return rel
@classmethod
def get_for_object(cls, obj):
return ArticleForObject.objects.get(object_id=obj.id, content_type=ContentType.objects.get_for_model(obj)).article
def __unicode__(self):
return self.title
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
class ObjectForArticle(models.Model): class ArticleForObject(models.Model):
article = models.ForeignKey('Article') article = models.ForeignKey('Article')
# Same as django.contrib.comments # Same as django.contrib.comments
content_type = models.ForeignKey(ContentType, content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'), verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s") related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID')) object_id = models.PositiveIntegerField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk") content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_id")
has_parent_method = models.BooleanField(default=False, editable=False)
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
verbose_name = _(u'Article for object')
verbose_name_plural = _(u'Articles for object')
# Do not allow several objects
unique_together = ('content_type', 'object_id')
class ArticleRevision(models.Model): class ArticleRevision(models.Model):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _, ugettext
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from mptt.models import MPTTModel from mptt.models import MPTTModel
from mptt.fields import TreeForeignKey from mptt.fields import TreeForeignKey
...@@ -8,15 +8,22 @@ from mptt.fields import TreeForeignKey ...@@ -8,15 +8,22 @@ from mptt.fields import TreeForeignKey
from wiki.core.exceptions import NoRootURL, MultipleRootURLs from wiki.core.exceptions import NoRootURL, MultipleRootURLs
from wiki.conf import settings from wiki.conf import settings
from article import Article
from wiki.models.article import ArticleRevision, ArticleForObject
from django.contrib.contenttypes import generic
from django.core.exceptions import ValidationError
class URLPath(MPTTModel): class URLPath(MPTTModel):
""" """
Strategy: Very few fields go here, as most has to be managed through an Strategy: Very few fields go here, as most has to be managed through an
article's revision. As a side-effect, the URL resolution remains slim and swift. article's revision. As a side-effect, the URL resolution remains slim and swift.
""" """
article = models.ForeignKey('wiki.Article', # Tells django-wiki that permissions from a URLPath object's article
verbose_name=_(u'article'), # should be inherited to children's articles
help_text=_(u'Article to be displayed for this path')) INHERIT_PERMISSIONS = True
slug = models.SlugField(verbose_name=_(u'slug'))
articles = generic.GenericRelation(ArticleForObject)
slug = models.SlugField(verbose_name=_(u'slug'), null=True, blank=True)
site = models.ForeignKey(Site) site = models.ForeignKey(Site)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children') parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
...@@ -24,10 +31,11 @@ class URLPath(MPTTModel): ...@@ -24,10 +31,11 @@ class URLPath(MPTTModel):
"/".join([obj.slug for obj in self.get_ancestors(include_self=True)]) "/".join([obj.slug for obj in self.get_ancestors(include_self=True)])
class MPTTMeta: class MPTTMeta:
order_insertion_by = ['slug'] pass
def __unicode__(self): def __unicode__(self):
return self.path path = self.get_path()
return path if path else ugettext(u"(root)")
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super(URLPath, self).save(*args, **kwargs) super(URLPath, self).save(*args, **kwargs)
...@@ -37,7 +45,17 @@ class URLPath(MPTTModel): ...@@ -37,7 +45,17 @@ class URLPath(MPTTModel):
verbose_name_plural = _(u'URL paths') verbose_name_plural = _(u'URL paths')
unique_together = ('site', 'parent', 'slug') unique_together = ('site', 'parent', 'slug')
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
def clean(self, *args, **kwargs):
if self.slug and not self.parent:
raise ValidationError(_(u'Sorry but you cannot have a root article with a slug.'))
if not self.slug and self.parent:
raise ValidationError(_(u'A non-root note must always have a slug.'))
if not self.parent:
if URLPath.objects.root_nodes().filter(site=self.site):
raise ValidationError(_(u'There is already a root node on %s') % self.site)
super(URLPath, self).clean(*args, **kwargs)
@classmethod @classmethod
def get_by_path(cls, path): def get_by_path(cls, path):
""" """
...@@ -45,28 +63,44 @@ class URLPath(MPTTModel): ...@@ -45,28 +63,44 @@ class URLPath(MPTTModel):
Accepts paths both starting with and without '/' Accepts paths both starting with and without '/'
""" """
site = Site.objects.get_current() site = Site.objects.get_current()
paths = cls.objects.filter(site=site) root_nodes = cls.objects.root_nodes().filter(site=site)
root_nodes = paths.filter(parent=None)
path = path.lstrip("/") path = path.lstrip("/")
no_paths = root_nodes.count()
if no_paths == 0:
raise NoRootURL
if no_paths > 1:
raise MultipleRootURLs
# Root page requested # Root page requested
if not path: if not path:
no_paths = root_nodes.count()
if no_paths == 0:
raise NoRootURL
if no_paths > 1:
raise MultipleRootURLs
return root_nodes[0] return root_nodes[0]
slugs = path.split('/') slugs = path.split('/')
level = 1
parent = root_nodes[0]
for slug in slugs: for slug in slugs:
if settings.URL_CASE_SENSITIVE: if settings.URL_CASE_SENSITIVE:
paths = paths.filter(parent__slug=slug) parent = parent.get_children.get(slug=slug)
else: else:
paths = paths.filter(parent__slug__iexact=slug) parent = parent.get_children.get(slug__iexact=slug)
level += 1
if paths.count() == 0: return parent
raise cls.DoesNotExist
@classmethod
return paths[0] def create_root(cls, site=None):
if not site: site = Site.objects.get_current()
\ No newline at end of file if not cls.objects.root_nodes().filter(site=site):
# (get_or_create does not work for MPTT models??)
root = cls.objects.create(site=site)
article = Article()
article.add_revision(ArticleRevision(), save=True)
article.add_object_relation(root)
@property
def article(self):
try:
return self.articles.all()[0]
except IndexError:
return None
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment