Repository: django-cms/django-cms

Issue:
Error when overwriting page with same url
I found this error in 2.1.3 and also tested same error in master branch.

Here is how to reproduce error:
1. Create page named 'boo'
2. Create page named 'foo'
3. In Advanced Page Settings, Overwrite URL field add value 'boo'
4. Visit page 'boo'
5. Exception will be raised:

<pre>
Traceback:
File "/tmp/djcms/env/lib/python2.7/site-packages/Django-1.3-py2.7.egg/django/core/handlers/base.py" in get_response
  89.                     response = middleware_method(request)
File "/tmp/djcms/django-cms/cms/middleware/toolbar.py" in process_request
  29.         request.toolbar = CMSToolbar(request)
File "/tmp/djcms/django-cms/cms/cms_toolbar.py" in __init__
  60.         self.init()
File "/tmp/djcms/django-cms/cms/cms_toolbar.py" in init
  64.         self.can_change = (self.request.current_page and
File "/tmp/djcms/django-cms/cms/middleware/page.py" in __get__
  8.             request._current_page_cache = get_page_from_request(request)
File "/tmp/djcms/django-cms/cms/utils/page_resolver.py" in get_page_from_request
  107.         page = pages.filter(q).distinct().get()
File "/tmp/djcms/env/lib/python2.7/site-packages/Django-1.3-py2.7.egg/django/db/models/query.py" in get
  351.                 % (self.model._meta.object_name, num, kwargs))

Exception Type: MultipleObjectsReturned at /boo/
Exception Value: get() returned more than one Page -- it returned 2! Lookup parameters were {}
</pre>


I know, that 2.2 version has 'Redirect' feature, but 'Overwrite URL' field
should be validated to prevent this error.



Discussion:
I can confirm this issue, looking into it now


Resolving patch:
diff --git a/cms/admin/forms.py b/cms/admin/forms.py
--- a/cms/admin/forms.py
+++ b/cms/admin/forms.py
@@ -1,4 +1,14 @@
 # -*- coding: utf-8 -*-
+from cms.apphook_pool import apphook_pool
+from cms.forms.widgets import UserSelectAdminWidget
+from cms.models import (Page, PagePermission, PageUser, ACCESS_PAGE, 
+    PageUserGroup)
+from cms.utils.mail import mail_page_user_change
+from cms.utils.page import is_valid_page_slug
+from cms.utils.page_resolver import get_page_from_path
+from cms.utils.permissions import (get_current_user, get_subordinate_users, 
+    get_subordinate_groups)
+from cms.utils.urlutils import any_path_re
 from django import forms
 from django.conf import settings
 from django.contrib.auth.forms import UserCreationForm
@@ -11,18 +21,9 @@
 from django.forms.widgets import HiddenInput
 from django.template.defaultfilters import slugify
 from django.utils.translation import ugettext_lazy as _, get_language
+from menus.menu_pool import menu_pool
 
-from cms.apphook_pool import apphook_pool
-from cms.forms.widgets import UserSelectAdminWidget
-from cms.models import (Page, PagePermission, PageUser,
-    ACCESS_PAGE, PageUserGroup)
-from cms.utils.mail import mail_page_user_change
-from cms.utils.page import is_valid_page_slug
-from cms.utils.permissions import (get_current_user, get_subordinate_users,
-    get_subordinate_groups)
-from cms.utils.urlutils import any_path_re
 
-from menus.menu_pool import menu_pool
 
 
 def get_permission_acessor(obj):
@@ -158,7 +159,7 @@ def __init__(self, *args, **kwargs):
             self.fields['navigation_extenders'].widget = forms.Select({}, [('', "---------")] + menu_pool.get_menus_by_attribute("cms_enabled", True))
         if 'application_urls' in self.fields:
             self.fields['application_urls'].choices = [('', "---------")] + apphook_pool.get_apphooks()
-    
+            
     def clean(self):
         cleaned_data = super(PageForm, self).clean()
         if 'reverse_id' in self.fields:
@@ -175,6 +176,8 @@ def clean_overwrite_url(self):
             if url:
                 if not any_path_re.match(url):
                     raise forms.ValidationError(_('Invalid URL, use /my/url format.'))
+                if get_page_from_path(url.strip('/')):
+                    raise forms.ValidationError(_('Page with redirect url %r already exist') % url)
         return url
 
 class PagePermissionInlineAdminForm(forms.ModelForm):


Test patch:
diff --git a/cms/tests/page.py b/cms/tests/page.py
--- a/cms/tests/page.py
+++ b/cms/tests/page.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 from __future__ import with_statement
+from cms.admin.forms import PageForm
 from cms.api import create_page
 from cms.models import Page, Title
 from cms.models.placeholdermodel import Placeholder
@@ -442,6 +443,22 @@ def test_page_already_expired(self):
                                publication_end_date=yesterday, published=True)
             resp = self.client.get(page.get_absolute_url('en'))
             self.assertEqual(resp.status_code, 404)
+    
+    def test_existing_overwrite_url(self):
+        with SettingsOverride(CMS_MODERATOR=False, CMS_PERMISSION=False):
+            create_page('home', 'nav_playground.html', 'en', published=True)
+            create_page('boo', 'nav_playground.html', 'en', published=True)
+            data = {
+                'title': 'foo',
+                'overwrite_url': '/boo/',
+                'slug': 'foo',
+                'language': 'en',
+                'template': 'nav_playground.html',
+                'site': 1,
+            }
+            form = PageForm(data)
+            self.assertFalse(form.is_valid())
+            self.assertTrue('overwrite_url' in form.errors)