Skip to content

Commit

Permalink
Added an option to the page model to be able to define the amount of …
Browse files Browse the repository at this point in the history
…subpages underneath a specific parent page
  • Loading branch information
leewesleyv authored and gasman committed Mar 15, 2019
1 parent 339b675 commit c831d43
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Changelog
* Added instructions on how to generate urls for `ModelAdmin` to documentation (LB (Ben Johnston), Andy Babic)
* Added option to specify a fallback URL on `{% pageurl %}` (Arthur Holzner)
* Add support for more rich text formats, disabled by default: `blockquote`, `superscript`, `subscript`, `strikethrough`, `code` (Md Arifin Ibne Matin)
* Added `max_count_per_parent` option on page models to limit the number of pages of a given type that can be created under one parent page (Wesley van Lee)
* Fix: Set `SERVER_PORT` to 443 in `Page.dummy_request()` for HTTPS sites (Sergey Fedoseev)
* Fix: Include port number in `Host` header of `Page.dummy_request()` (Sergey Fedoseev)
* Fix: Validation error messages in `InlinePanel` no longer count towards `max_num` when disabling the 'add' button (Todd Dembrey, Thibaud Colas)
Expand Down
4 changes: 4 additions & 0 deletions docs/reference/pages/model_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ In addition to the model fields provided, ``Page`` has many properties and metho

Controls the maximum number of pages of this type that can be created through the Wagtail administration interface. This is useful when needing "allow at most 3 of these pages to exist", or for singleton pages.

.. attribute:: max_count_per_parent

Controls the maximum number of pages of this type that can be created under any one parent page.

.. attribute:: exclude_fields_in_copy

An array of field names that will not be included when a Page is copied.
Expand Down
1 change: 1 addition & 0 deletions docs/releases/2.5.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Other features
* Added instructions on how to generate urls for ``ModelAdmin`` to documentation (LB (Ben Johnston), Andy Babic)
* Added option to specify a fallback URL on ``{% pageurl %}`` (Arthur Holzner)
* Add support for more rich text formats, disabled by default: ``blockquote``, ``superscript``, ``subscript``, ``strikethrough``, ``code`` (Md Arifin Ibne Matin)
* Added ``max_count_per_parent`` option on page models to limit the number of pages of a given type that can be created under one parent page (Wesley van Lee)


Bug fixes
Expand Down
6 changes: 6 additions & 0 deletions wagtail/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,9 @@ class Page(AbstractPage, index.Indexed, ClusterableModel, metaclass=PageBase):
# Define the maximum number of instances this page type can have. Default to unlimited.
max_count = None

# Define the maximum number of instances this page can have under a specific parent. Default to unlimited.
max_count_per_parent = None

# An array of additional field names that will not be included when a Page is copied.
exclude_fields_in_copy = []

Expand Down Expand Up @@ -980,6 +983,9 @@ def can_create_at(cls, parent):
if cls.max_count is not None:
can_create = can_create and cls.objects.count() < cls.max_count

if cls.max_count_per_parent is not None:
can_create = can_create and parent.get_children().type(cls).count() < cls.max_count_per_parent

return can_create

def can_move_to(self, parent):
Expand Down
21 changes: 19 additions & 2 deletions wagtail/core/tests/test_page_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
AbstractPage, Advert, AlwaysShowInMenusPage, BlogCategory, BlogCategoryBlogPage, BusinessChild,
BusinessIndex, BusinessNowherePage, BusinessSubIndex, CustomManager, CustomManagerPage,
CustomPageQuerySet, EventCategory, EventIndex, EventPage, GenericSnippetPage, ManyToManyBlogPage,
MTIBasePage, MTIChildPage, MyCustomPage, OneToOnePage, PageWithExcludedCopyField, SimplePage,
SingleEventPage, SingletonPage, StandardIndex, TaggedPage)
MTIBasePage, MTIChildPage, MyCustomPage, OneToOnePage, PageWithExcludedCopyField, SimpleChildPage,
SimplePage, SimpleParentPage, SingleEventPage, SingletonPage, StandardIndex, TaggedPage)
from wagtail.tests.utils import WagtailTestUtils


Expand Down Expand Up @@ -1167,6 +1167,23 @@ def test_can_create_at(self):
self.assertFalse(BusinessChild.can_create_at(SimplePage()))
self.assertFalse(BusinessSubIndex.can_create_at(SimplePage()))

def test_can_create_at_with_max_count_per_parent_limited_to_one(self):
root_page = Page.objects.get(url_path='/home/')

# Create 2 parent pages for our limited page model
parent1 = root_page.add_child(instance=SimpleParentPage(title='simple parent', slug='simple-parent'))
parent2 = root_page.add_child(instance=SimpleParentPage(title='simple parent', slug='simple-parent-2'))

# Add a child page to one of the pages (assert just to be sure)
self.assertTrue(SimpleChildPage.can_create_at(parent1))
parent1.add_child(instance=SimpleChildPage(title='simple child', slug='simple-child'))

# We already have a `SimpleChildPage` as a child of `parent1`, and since it is limited
# to have only 1 child page, we cannot create anoter one. However, we should still be able
# to create an instance for this page at a different location (as child of `parent2`)
self.assertFalse(SimpleChildPage.can_create_at(parent1))
self.assertTrue(SimpleChildPage.can_create_at(parent2))

def test_can_move_to(self):
self.assertTrue(SimplePage().can_move_to(SimplePage()))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Generated by Django 2.1.7 on 2019-03-15 10:39

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('wagtailcore', '0041_group_collection_permissions_verbose_name_plural'),
('tests', '0041_secretpage'),
]

operations = [
migrations.CreateModel(
name='SimpleChildPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='SimpleParentPage',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]
12 changes: 12 additions & 0 deletions wagtail/tests/testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1327,3 +1327,15 @@ class SecretPage(PerUserPageMixin, Page):
superuser_content_panels = basic_content_panels + [
FieldPanel('secret_data'),
]


class SimpleParentPage(Page):
# `BusinessIndex` has been added to bring it in line with other tests
subpage_types = ['tests.SimpleChildPage', BusinessIndex]


class SimpleChildPage(Page):
# `Page` has been added to bring it in line with other tests
parent_page_types = ['tests.SimpleParentPage', Page]

max_count_per_parent = 1

0 comments on commit c831d43

Please sign in to comment.