Skip to content

Commit 5ca529d

Browse files
committed
First working version, extracted from django-fluent-pages
- Using same registration system - Adapted base classes and templates to be fully generic - Added nodetype-... class to tree view.
0 parents  commit 5ca529d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+5742
-0
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*.pyc
2+
*.pyo
3+
*.mo
4+
*.db
5+
.idea
6+
.project
7+
.pydevproject
8+
.idea/workspace.xml
9+
.DS_Store
10+
dist/
11+
docs/_build/

LICENSE.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=======
2+
LICENSE
3+
=======
4+
5+
Copyright © 2011, Edoburu
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this software except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.

README.rst

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
django-polymorphic-tree
2+
=======================
3+
4+
This is a stand alone module, which provides:
5+
6+
" A polymorphic structure to display content in a tree. "
7+
8+
In other words, this module provides a node tree, where each node type can be a different model.
9+
This allows you to structure your site tree as you see fit. For example:
10+
11+
* Build a tree of a root node, category nodes, leaf nodes, each with custom fields.
12+
13+
Origin
14+
------
15+
16+
This module was extracted out of django-fluent-pages_ because it turned out to serve a generic purpose.
17+
This was done during contact work at Leukeleu_ (also known for their involvement in django-fiber_).
18+
19+
20+
Installation
21+
============
22+
23+
First install the module, preferably in a virtual environment::
24+
25+
pip install -e git+https://github.com/edoburu/django-polymorphic-tree.git#egg=django-polymorphic-tree
26+
27+
The main dependencies are django-mptt_ and django-polymorphic_,
28+
which will be automatically installed.
29+
30+
Configuration
31+
-------------
32+
33+
Next, create a project which uses the CMS::
34+
35+
cd ..
36+
django-admin.py startproject demo
37+
38+
Add the following to ``settings.py``::
39+
40+
INSTALLED_APPS += (
41+
'polymorphic_tree',
42+
'polymorphic',
43+
'mptt',
44+
)
45+
46+
The database can be created afterwards::
47+
48+
./manage.py syncdb
49+
./manage.py runserver
50+
51+
52+
Custom node types
53+
-----------------
54+
55+
The main feature of this module is the support for custom node types.
56+
It boils down to creating a package with 2 files:
57+
58+
The ``models.py`` file should define the custom node type, and any fields it has::
59+
60+
from django.db import models
61+
from django.utils.translation import ugettext_lazy as _
62+
from fluent_pages.models import HtmlPage
63+
from mysite.settings import RST_TEMPLATE_CHOICES
64+
65+
66+
class CategoryNode(PolymorphicMPTTModel):
67+
"""
68+
A page that renders RST code.
69+
"""
70+
title = models.CharField(_("Title"), max_length=200)
71+
72+
class Meta:
73+
verbose_name = _("Category node")
74+
verbose_name_plural = _("Category node")
75+
76+
A ``node_type_plugins.py`` file that defines the metadata, and additional methods (e.g. rendering)::
77+
78+
from polymorphic_tree.extensions import NodeTypePlugin, node_type_pool
79+
from .models import CategoryNode
80+
81+
82+
@node_type_pool.register
83+
class CategoryNodePlugin(NodeTypePlugin):
84+
model = CategoryNode
85+
sort_priority = 10
86+
87+
def render(self, request, categorynode):
88+
return 'demo'
89+
90+
Optionally, a ``model_admin`` can also be defined, to have custom field layouts or extra functionality in the *edit* or *delete* page.
91+
92+
Plugin configuration
93+
~~~~~~~~~~~~~~~~~~~~
94+
95+
The plugin can define the following attributes:
96+
97+
* ``model`` - the model for the page type
98+
* ``model_admin`` - the custom admin to use (must inherit from ``PageAdmin``)
99+
* ``can_have_children`` - whether the node type allows to have child nodes.
100+
* ``urls`` - a custom set of URL patterns for sub pages (either a module name, or ``patterns()`` result).
101+
* ``sort_priority`` - a sorting order in the "add page" dialog.
102+
103+
104+
Contributing
105+
------------
106+
107+
This module is designed to be generic. In case there is anything you didn't like about it,
108+
or think it's not flexible enough, please let us know. We'd love to improve it!
109+
110+
If you have any other valuable contribution, suggestion or idea,
111+
please let us know as well because we will look into it.
112+
Pull requests are welcome too. :-)
113+
114+
115+
.. _Leukeleu: http://www.leukeleu.nl/
116+
.. _django-fiber: https://github.com/ridethepony/django-fiber
117+
.. _django-fluent-pages: https://github.com/edoburu/django-fluent-blogs
118+
.. _django-mptt: https://github.com/django-mptt/django-mptt
119+
.. _django-polymorphic: https://github.com/chrisglass/django_polymorphic
120+

polymorphic_tree/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
VERSION = (0, 1, 0)
2+
3+
# Do some sane version checking
4+
import django
5+
import mptt
6+
7+
if django.VERSION < (1,3,0):
8+
raise ImportError("At least Django 1.3.0 is required to run this application")
9+
10+
if mptt.VERSION < (0,4,0):
11+
raise ImportError("At least django-mptt 0.4.0 is required to run this application")

polymorphic_tree/admin/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from polymorphic_tree.admin.childadmin import PolymorphicMPTTChildModelAdmin, PolymorpicMPTTAdminForm
2+
from polymorphic_tree.admin.parentadmin import PolymorphicMPTTParentModelAdmin, NodeTypeChoiceForm
3+
4+
__all__ = (
5+
'PolymorphicMPTTChildModelAdmin', 'PolymorpicMPTTAdminForm',
6+
'PolymorphicMPTTParentModelAdmin', 'NodeTypeChoiceForm',
7+
)

polymorphic_tree/admin/childadmin.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
from django.utils.functional import lazy
2+
from mptt.admin import MPTTModelAdmin
3+
from mptt.forms import MPTTAdminForm
4+
from polymorphic_tree.utils.polymorphicadmin import PolymorphicChildModelAdmin
5+
from polymorphic_tree.models import PolymorphicMPTTModel
6+
7+
8+
class PolymorpicMPTTAdminForm(MPTTAdminForm):
9+
pass
10+
11+
12+
class PolymorphicMPTTChildModelAdmin(PolymorphicChildModelAdmin, MPTTModelAdmin):
13+
"""
14+
The internal machinery
15+
The admin screen for the ``PolymorphicMPTTModel`` objects.
16+
"""
17+
base_model = PolymorphicMPTTModel
18+
base_form = PolymorpicMPTTAdminForm
19+
base_fieldsets = None
20+
21+
22+
# NOTE: list page is configured in PolymorphicMPTTParentModelAdmin
23+
# as that class is used for the real admin screen in the edit/delete view.
24+
# This class is only a base class for the custom node type plugins.
25+
26+
27+
# ---- Pass parent_object to templates ----
28+
29+
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
30+
# Get parent object for breadcrumb
31+
parent_object = None
32+
parent_id = request.REQUEST.get('parent')
33+
if add and parent_id:
34+
assert not self.base_model._meta.abstract, "should define {0}.base_model".format(self.__class__.__name__)
35+
parent_object = self.base_model.objects.get(pk=int(parent_id)) # is polymorphic
36+
elif change:
37+
parent_object = obj.parent
38+
39+
# Pass parent object to the view.
40+
# Also allows to improve the breadcrumb for example
41+
context.update({
42+
'parent_object': parent_object,
43+
'parent_breadcrumb': lazy(lambda: list(parent_object.get_ancestors()) + [parent_object], list)() if parent_object else [],
44+
})
45+
46+
return super(PolymorphicMPTTChildModelAdmin, self).render_change_form(request, context, add=add, change=change, form_url=form_url, obj=obj)
47+
48+
49+
def delete_view(self, request, object_id, context=None):
50+
# Get parent object for breadcrumb
51+
parent_object = None
52+
try:
53+
assert not self.base_model._meta.abstract, "should define {0}.base_model".format(self.__class__.__name__)
54+
parent_pk = self.base_model.objects.non_polymorphic().values('parent').filter(pk=int(object_id))
55+
parent_object = self.base_model.objects.get(pk=parent_pk)
56+
except PolymorphicMPTTModel.DoesNotExist:
57+
pass
58+
59+
# Pass parent object to the view.
60+
# Also allows to improve the breadcrumb for example
61+
extra_context = {
62+
'parent_object': parent_object,
63+
'parent_breadcrumb': lazy(lambda: list(parent_object.get_ancestors()) + [parent_object], list)(),
64+
}
65+
extra_context.update(context or {})
66+
67+
return super(PolymorphicMPTTChildModelAdmin, self).delete_view(request, object_id, extra_context)

0 commit comments

Comments
 (0)