prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Duplicate page created
  On https://wafertest.debconf.org, I created the following page: https://wafertest.debconf.org/debconf-16-bursaries-instructions, when my Wafer pages page loaded, I saw that there existed two new pages with that title.
  
  When I visit https://wafertest.debconf.org/debconf-16-bursaries-instructions, wafer gives me a debug page that says "get() returned more than one Page -- it returned 2!"
  
  Here is the traceback: http://paste.debian.net/415666/
  
  For now I'll just delete the duplicate page, but @stefanor  mentioned that a unique index for pages may be required.
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `wafer/pages/admin.py`
  Content:
  ```
  1 from django.contrib import admin
  2 
  3 from wafer.pages.models import File, Page
  4 
  5 from wafer.compare.admin import CompareVersionAdmin, DateModifiedFilter
  6 
  7 
  8 class PageAdmin(CompareVersionAdmin, admin.ModelAdmin):
  9     prepopulated_fields = {"slug": ("name",)}
  10     list_display = ('name', 'slug', 'get_people_display_names', 'get_in_schedule')
  11 
  12     list_filter = (DateModifiedFilter,)
  13 
  14 
  15 
  16 admin.site.register(Page, PageAdmin)
  17 admin.site.register(File)
  18
  ```
  
  Path: `wafer/pages/models.py`
  Content:
  ```
  1 import logging
  2 logger = logging.getLogger(__name__)
  3 
  4 from django.utils.translation import ugettext_lazy as _
  5 from django.core.urlresolvers import reverse
  6 from django.conf import settings
  7 from django.db import models
  8 from django.db.models.signals import post_save
  9 from django.utils.encoding import python_2_unicode_compatible
  10 
  11 
  12 from markitup.fields import MarkupField
  13 from wafer.menu import MenuError, refresh_menu_cache
  14 
  15 
  16 @python_2_unicode_compatible
  17 class File(models.Model):
  18     """A file for use in page markup."""
  19     name = models.CharField(max_length=255)
  20     description = models.TextField()
  21     item = models.FileField(upload_to='pages_files')
  22 
  23     def __str__(self):
  24         return u'%s' % (self.name,)
  25 
  26 
  27 @python_2_unicode_compatible
  28 class Page(models.Model):
  29     """An extra page for the site."""
  30     name = models.CharField(max_length=255)
  31     slug = models.SlugField(help_text=_("Last component of the page URL"))
  32     parent = models.ForeignKey('self', null=True, blank=True)
  33     content = MarkupField(
  34         help_text=_("Markdown contents for the page."))
  35     include_in_menu = models.BooleanField(
  36         help_text=_("Whether to include in menus."),
  37         default=False)
  38     exclude_from_static = models.BooleanField(
  39         help_text=_("Whether to exclude this page from the static version of"
  40                     " the site (Container pages, etc.)"),
  41         default=False)
  42     files = models.ManyToManyField(
  43         File, related_name="pages", blank=True,
  44         help_text=_("Images and other files for use in"
  45                     " the content markdown field."))
  46 
  47     people = models.ManyToManyField(settings.AUTH_USER_MODEL,
  48         related_name='pages', blank=True,
  49         help_text=_("People associated with this page for display in the"
  50                     " schedule (Session chairs, panelists, etc.)"))
  51 
  52     def __str__(self):
  53         return u'%s' % (self.name,)
  54 
  55     def get_path(self):
  56         path, parent = [self.slug], self.parent
  57         while parent is not None:
  58             path.insert(0, parent.slug)
  59             parent = parent.parent
  60         return path
  61 
  62     def get_absolute_url(self):
  63         url = "/".join(self.get_path())
  64         return reverse('wafer_page', args=(url,))
  65 
  66     def get_in_schedule(self):
  67         if self.scheduleitem_set.all():
  68             return True
  69         return False
  70 
  71     def get_people_display_names(self):
  72         names = [person.userprofile.display_name()
  73                  for person in self.people.all()]
  74         if len(names) > 2:
  75             comma_names = ', '.join(names[:-1])
  76             return comma_names + ' and ' + names[-1]
  77         else:
  78             return ' and '.join(names)
  79 
  80     get_in_schedule.short_description = 'Added to schedule'
  81     get_in_schedule.boolean = True
  82 
  83     get_people_display_names.short_description = 'People'
  84 
  85     class Model:
  86         unique_together = (('parent', 'slug'),)
  87 
  88 
  89 def page_menus(root_menu):
  90     """Add page menus."""
  91     for page in Page.objects.filter(include_in_menu=True):
  92         path = page.get_path()
  93         menu = path[0] if len(path) > 1 else None
  94         try:
  95             root_menu.add_item(page.name, page.get_absolute_url(), menu=menu)
  96         except MenuError as e:
  97             logger.error("Bad menu item %r for page with slug %r."
  98                          % (e, page.slug))
  99 
  100 
  101 post_save.connect(refresh_menu_cache, sender=Page)
  102
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/wafer/pages/admin.py b/wafer/pages/admin.py
  --- a/wafer/pages/admin.py
  +++ b/wafer/pages/admin.py
  @@ -7,7 +7,8 @@
   
   class PageAdmin(CompareVersionAdmin, admin.ModelAdmin):
       prepopulated_fields = {"slug": ("name",)}
  -    list_display = ('name', 'slug', 'get_people_display_names', 'get_in_schedule')
  +    list_display = ('name', 'slug', 'get_absolute_url',
  +                    'get_people_display_names', 'get_in_schedule')
   
       list_filter = (DateModifiedFilter,)
   
  diff --git a/wafer/pages/models.py b/wafer/pages/models.py
  --- a/wafer/pages/models.py
  +++ b/wafer/pages/models.py
  @@ -3,6 +3,7 @@
   
   from django.utils.translation import ugettext_lazy as _
   from django.core.urlresolvers import reverse
  +from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
   from django.conf import settings
   from django.db import models
   from django.db.models.signals import post_save
  @@ -63,6 +64,8 @@
           url = "/".join(self.get_path())
           return reverse('wafer_page', args=(url,))
   
  +    get_absolute_url.short_description = 'page url'
  +
       def get_in_schedule(self):
           if self.scheduleitem_set.all():
               return True
  @@ -85,6 +88,35 @@
       class Model:
           unique_together = (('parent', 'slug'),)
   
  +    def clean(self):
  +        keys = [self.pk]
  +        parent = self.parent
  +        while parent is not None:
  +            if parent.pk in keys:
  +                raise ValidationError(
  +                    {
  +                        NON_FIELD_ERRORS: [
  +                            _("Circular reference in parent."),
  +                        ],
  +                    })
  +            keys.append(parent.pk)
  +            parent = parent.parent
  +        return super(Page, self).clean()
  +
  +    def validate_unique(self, exclude=None):
  +        existing = Page.objects.filter(slug=self.slug, parent=self.parent)
  +        # We could be updating the page, so don't fail if the existing
  +        # entry is this page.
  +        if existing.count() > 1 or (existing.count() == 1 and
  +                                    existing.first().pk != self.pk):
  +            raise ValidationError(
  +                {
  +                    NON_FIELD_ERRORS: [
  +                        _("Duplicate parent/slug combination."),
  +                    ],
  +                })
  +        return super(Page, self).validate_unique(exclude)
  +
   
   def page_menus(root_menu):
       """Add page menus."""
  
verification_info: {"golden_diff": "diff --git a/wafer/pages/admin.py b/wafer/pages/admin.py\n--- a/wafer/pages/admin.py\n+++ b/wafer/pages/admin.py\n@@ -7,7 +7,8 @@\n \n class PageAdmin(CompareVersionAdmin, admin.ModelAdmin):\n     prepopulated_fields = {\"slug\": (\"name\",)}\n-    list_display = ('name', 'slug', 'get_people_display_names', 'get_in_schedule')\n+    list_display = ('name', 'slug', 'get_absolute_url',\n+                    'get_people_display_names', 'get_in_schedule')\n \n     list_filter = (DateModifiedFilter,)\n \ndiff --git a/wafer/pages/models.py b/wafer/pages/models.py\n--- a/wafer/pages/models.py\n+++ b/wafer/pages/models.py\n@@ -3,6 +3,7 @@\n \n from django.utils.translation import ugettext_lazy as _\n from django.core.urlresolvers import reverse\n+from django.core.exceptions import ValidationError, NON_FIELD_ERRORS\n from django.conf import settings\n from django.db import models\n from django.db.models.signals import post_save\n@@ -63,6 +64,8 @@\n         url = \"/\".join(self.get_path())\n         return reverse('wafer_page', args=(url,))\n \n+    get_absolute_url.short_description = 'page url'\n+\n     def get_in_schedule(self):\n         if self.scheduleitem_set.all():\n             return True\n@@ -85,6 +88,35 @@\n     class Model:\n         unique_together = (('parent', 'slug'),)\n \n+    def clean(self):\n+        keys = [self.pk]\n+        parent = self.parent\n+        while parent is not None:\n+            if parent.pk in keys:\n+                raise ValidationError(\n+                    {\n+                        NON_FIELD_ERRORS: [\n+                            _(\"Circular reference in parent.\"),\n+                        ],\n+                    })\n+            keys.append(parent.pk)\n+            parent = parent.parent\n+        return super(Page, self).clean()\n+\n+    def validate_unique(self, exclude=None):\n+        existing = Page.objects.filter(slug=self.slug, parent=self.parent)\n+        # We could be updating the page, so don't fail if the existing\n+        # entry is this page.\n+        if existing.count() > 1 or (existing.count() == 1 and\n+                                    existing.first().pk != self.pk):\n+            raise ValidationError(\n+                {\n+                    NON_FIELD_ERRORS: [\n+                        _(\"Duplicate parent/slug combination.\"),\n+                    ],\n+                })\n+        return super(Page, self).validate_unique(exclude)\n+\n \n def page_menus(root_menu):\n     \"\"\"Add page menus.\"\"\"\n", "issue": "Duplicate page created\nOn https://wafertest.debconf.org, I created the following page: https://wafertest.debconf.org/debconf-16-bursaries-instructions, when my Wafer pages page loaded, I saw that there existed two new pages with that title.\n\nWhen I visit https://wafertest.debconf.org/debconf-16-bursaries-instructions, wafer gives me a debug page that says \"get() returned more than one Page -- it returned 2!\"\n\nHere is the traceback: http://paste.debian.net/415666/\n\nFor now I'll just delete the duplicate page, but @stefanor  mentioned that a unique index for pages may be required.\n\n", "before_files": [{"content": "from django.contrib import admin\n\nfrom wafer.pages.models import File, Page\n\nfrom wafer.compare.admin import CompareVersionAdmin, DateModifiedFilter\n\n\nclass PageAdmin(CompareVersionAdmin, admin.ModelAdmin):\n    prepopulated_fields = {\"slug\": (\"name\",)}\n    list_display = ('name', 'slug', 'get_people_display_names', 'get_in_schedule')\n\n    list_filter = (DateModifiedFilter,)\n\n\n\nadmin.site.register(Page, PageAdmin)\nadmin.site.register(File)\n", "path": "wafer/pages/admin.py"}, {"content": "import logging\nlogger = logging.getLogger(__name__)\n\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.core.urlresolvers import reverse\nfrom django.conf import settings\nfrom django.db import models\nfrom django.db.models.signals import post_save\nfrom django.utils.encoding import python_2_unicode_compatible\n\n\nfrom markitup.fields import MarkupField\nfrom wafer.menu import MenuError, refresh_menu_cache\n\n\n@python_2_unicode_compatible\nclass File(models.Model):\n    \"\"\"A file for use in page markup.\"\"\"\n    name = models.CharField(max_length=255)\n    description = models.TextField()\n    item = models.FileField(upload_to='pages_files')\n\n    def __str__(self):\n        return u'%s' % (self.name,)\n\n\n@python_2_unicode_compatible\nclass Page(models.Model):\n    \"\"\"An extra page for the site.\"\"\"\n    name = models.CharField(max_length=255)\n    slug = models.SlugField(help_text=_(\"Last component of the page URL\"))\n    parent = models.ForeignKey('self', null=True, blank=True)\n    content = MarkupField(\n        help_text=_(\"Markdown contents for the page.\"))\n    include_in_menu = models.BooleanField(\n        help_text=_(\"Whether to include in menus.\"),\n        default=False)\n    exclude_from_static = models.BooleanField(\n        help_text=_(\"Whether to exclude this page from the static version of\"\n                    \" the site (Container pages, etc.)\"),\n        default=False)\n    files = models.ManyToManyField(\n        File, related_name=\"pages\", blank=True,\n        help_text=_(\"Images and other files for use in\"\n                    \" the content markdown field.\"))\n\n    people = models.ManyToManyField(settings.AUTH_USER_MODEL,\n        related_name='pages', blank=True,\n        help_text=_(\"People associated with this page for display in the\"\n                    \" schedule (Session chairs, panelists, etc.)\"))\n\n    def __str__(self):\n        return u'%s' % (self.name,)\n\n    def get_path(self):\n        path, parent = [self.slug], self.parent\n        while parent is not None:\n            path.insert(0, parent.slug)\n            parent = parent.parent\n        return path\n\n    def get_absolute_url(self):\n        url = \"/\".join(self.get_path())\n        return reverse('wafer_page', args=(url,))\n\n    def get_in_schedule(self):\n        if self.scheduleitem_set.all():\n            return True\n        return False\n\n    def get_people_display_names(self):\n        names = [person.userprofile.display_name()\n                 for person in self.people.all()]\n        if len(names) > 2:\n            comma_names = ', '.join(names[:-1])\n            return comma_names + ' and ' + names[-1]\n        else:\n            return ' and '.join(names)\n\n    get_in_schedule.short_description = 'Added to schedule'\n    get_in_schedule.boolean = True\n\n    get_people_display_names.short_description = 'People'\n\n    class Model:\n        unique_together = (('parent', 'slug'),)\n\n\ndef page_menus(root_menu):\n    \"\"\"Add page menus.\"\"\"\n    for page in Page.objects.filter(include_in_menu=True):\n        path = page.get_path()\n        menu = path[0] if len(path) > 1 else None\n        try:\n            root_menu.add_item(page.name, page.get_absolute_url(), menu=menu)\n        except MenuError as e:\n            logger.error(\"Bad menu item %r for page with slug %r.\"\n                         % (e, page.slug))\n\n\npost_save.connect(refresh_menu_cache, sender=Page)\n", "path": "wafer/pages/models.py"}], "after_files": [{"content": "from django.contrib import admin\n\nfrom wafer.pages.models import File, Page\n\nfrom wafer.compare.admin import CompareVersionAdmin, DateModifiedFilter\n\n\nclass PageAdmin(CompareVersionAdmin, admin.ModelAdmin):\n    prepopulated_fields = {\"slug\": (\"name\",)}\n    list_display = ('name', 'slug', 'get_absolute_url',\n                    'get_people_display_names', 'get_in_schedule')\n\n    list_filter = (DateModifiedFilter,)\n\n\n\nadmin.site.register(Page, PageAdmin)\nadmin.site.register(File)\n", "path": "wafer/pages/admin.py"}, {"content": "import logging\nlogger = logging.getLogger(__name__)\n\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.core.urlresolvers import reverse\nfrom django.core.exceptions import ValidationError, NON_FIELD_ERRORS\nfrom django.conf import settings\nfrom django.db import models\nfrom django.db.models.signals import post_save\nfrom django.utils.encoding import python_2_unicode_compatible\n\n\nfrom markitup.fields import MarkupField\nfrom wafer.menu import MenuError, refresh_menu_cache\n\n\n@python_2_unicode_compatible\nclass File(models.Model):\n    \"\"\"A file for use in page markup.\"\"\"\n    name = models.CharField(max_length=255)\n    description = models.TextField()\n    item = models.FileField(upload_to='pages_files')\n\n    def __str__(self):\n        return u'%s' % (self.name,)\n\n\n@python_2_unicode_compatible\nclass Page(models.Model):\n    \"\"\"An extra page for the site.\"\"\"\n    name = models.CharField(max_length=255)\n    slug = models.SlugField(help_text=_(\"Last component of the page URL\"))\n    parent = models.ForeignKey('self', null=True, blank=True)\n    content = MarkupField(\n        help_text=_(\"Markdown contents for the page.\"))\n    include_in_menu = models.BooleanField(\n        help_text=_(\"Whether to include in menus.\"),\n        default=False)\n    exclude_from_static = models.BooleanField(\n        help_text=_(\"Whether to exclude this page from the static version of\"\n                    \" the site (Container pages, etc.)\"),\n        default=False)\n    files = models.ManyToManyField(\n        File, related_name=\"pages\", blank=True,\n        help_text=_(\"Images and other files for use in\"\n                    \" the content markdown field.\"))\n\n    people = models.ManyToManyField(settings.AUTH_USER_MODEL,\n        related_name='pages', blank=True,\n        help_text=_(\"People associated with this page for display in the\"\n                    \" schedule (Session chairs, panelists, etc.)\"))\n\n    def __str__(self):\n        return u'%s' % (self.name,)\n\n    def get_path(self):\n        path, parent = [self.slug], self.parent\n        while parent is not None:\n            path.insert(0, parent.slug)\n            parent = parent.parent\n        return path\n\n    def get_absolute_url(self):\n        url = \"/\".join(self.get_path())\n        return reverse('wafer_page', args=(url,))\n\n    get_absolute_url.short_description = 'page url'\n\n    def get_in_schedule(self):\n        if self.scheduleitem_set.all():\n            return True\n        return False\n\n    def get_people_display_names(self):\n        names = [person.userprofile.display_name()\n                 for person in self.people.all()]\n        if len(names) > 2:\n            comma_names = ', '.join(names[:-1])\n            return comma_names + ' and ' + names[-1]\n        else:\n            return ' and '.join(names)\n\n    get_in_schedule.short_description = 'Added to schedule'\n    get_in_schedule.boolean = True\n\n    get_people_display_names.short_description = 'People'\n\n    class Model:\n        unique_together = (('parent', 'slug'),)\n\n    def clean(self):\n        keys = [self.pk]\n        parent = self.parent\n        while parent is not None:\n            if parent.pk in keys:\n                raise ValidationError(\n                    {\n                        NON_FIELD_ERRORS: [\n                            _(\"Circular reference in parent.\"),\n                        ],\n                    })\n            keys.append(parent.pk)\n            parent = parent.parent\n        return super(Page, self).clean()\n\n    def validate_unique(self, exclude=None):\n        existing = Page.objects.filter(slug=self.slug, parent=self.parent)\n        # We could be updating the page, so don't fail if the existing\n        # entry is this page.\n        if existing.count() > 1 or (existing.count() == 1 and\n                                    existing.first().pk != self.pk):\n            raise ValidationError(\n                {\n                    NON_FIELD_ERRORS: [\n                        _(\"Duplicate parent/slug combination.\"),\n                    ],\n                })\n        return super(Page, self).validate_unique(exclude)\n\n\ndef page_menus(root_menu):\n    \"\"\"Add page menus.\"\"\"\n    for page in Page.objects.filter(include_in_menu=True):\n        path = page.get_path()\n        menu = path[0] if len(path) > 1 else None\n        try:\n            root_menu.add_item(page.name, page.get_absolute_url(), menu=menu)\n        except MenuError as e:\n            logger.error(\"Bad menu item %r for page with slug %r.\"\n                         % (e, page.slug))\n\n\npost_save.connect(refresh_menu_cache, sender=Page)\n", "path": "wafer/pages/models.py"}]}