diff --git a/clktc/links/models.py b/clktc/links/models.py index ee00904..56a5828 100644 --- a/clktc/links/models.py +++ b/clktc/links/models.py @@ -1,8 +1,14 @@ from django.contrib.auth.models import User from django.contrib.sites.models import Site +from django.core.exceptions import ValidationError from django.db import models -# Create your models here. + +class LinkWithShortUrlAlreadyExistsForSite(ValidationError): + def __init__(self): + super(LinkWithShortUrlAlreadyExistsForSite, self).__init__({ + 'short_url': ["Link with this short URL already exists for this site. Please try another one."] + }) class Link(models.Model): destination_url = models.URLField("Destination", max_length=255, blank=False, verify_exists=False) @@ -17,7 +23,19 @@ class Meta: def url(self): return "http://%s/%s" % (self.site.domain, self.short_url) + def is_unique(self, matching): + if not len(matching): + return True + elif self.pk: + if len(matching) == 1: + other = matching.get() + return other.pk == self.pk + def save(self, *args, **kw): + if self.short_url and self.site_id: + matching = Link.objects.filter(short_url=self.short_url, site=self.site) + if not self.is_unique(matching): + raise LinkWithShortUrlAlreadyExistsForSite() self.full_clean() super(Link, self).save(*args, **kw) diff --git a/clktc/links/tests/test_models.py b/clktc/links/tests/test_models.py index f72158a..350556f 100644 --- a/clktc/links/tests/test_models.py +++ b/clktc/links/tests/test_models.py @@ -47,7 +47,7 @@ def test_site_and_short_url_are_unique_together(self): Model.save(l1) Model.save(l2) - @raises_regexp(ValidationError, 'Link with this Short URL and Site already exists') + @raises_regexp(ValidationError, 'Link with this short URL already exists for this site. Please try another one.') def test_site_and_short_url_are_unique_together(self): Link(destination_url = "http://example.com", short_url = "foobarbaz", site = self.site).save() Link(destination_url = "http://example.com", short_url = "foobarbaz", site = self.site).save() diff --git a/clktc/links/tests/test_views.py b/clktc/links/tests/test_views.py index 5fcdaa6..783f0c3 100644 --- a/clktc/links/tests/test_views.py +++ b/clktc/links/tests/test_views.py @@ -64,6 +64,12 @@ def test_adding_a_link_without_short_url(self): response = self.client.post('/l/add/', {"destination_url" : EXAMPLE_URL, "short_url" : ""}) self.assertFormError(response, 'form', 'short_url', u'This field is required.') + def test_adding_a_link_with_duplicate_short_url(self): + link = given_a_link(self.site, user=self.user) + response = self.client.post('/l/add/', {"destination_url" : "http://foo.bar/baz", "short_url": link.short_url}) + self.assertFormError(response, 'form', 'short_url', u'Link with this short URL already exists for this site. Please try another one.') + + class EditLinkTest(ViewBaseTest): def test_edit_link_returns_404_if_current_owner_does_not_match(self): link = given_a_link(self.site) @@ -84,7 +90,7 @@ def test_save_on_edit_link_updates_link_with_new_details(self): def test_save_on_edit_link_redirects_to_all_links(self): link = given_a_link(self.site, user=self.user) - response = self.client.post("/l/edit/%s" % link.pk, {"destination_url" : "http://example.org", "short_url" : "example2"}) + response = self.client.post("/l/edit/%s" % link.pk, {"destination_url" : "http://example.org"}) self.assertRedirects(response, ALL_LINKS) class VisitLinkTest(ViewBaseTest): diff --git a/clktc/links/views.py b/clktc/links/views.py index 937863d..b25422c 100644 --- a/clktc/links/views.py +++ b/clktc/links/views.py @@ -1,5 +1,6 @@ # Create your views here. from django.contrib.auth.decorators import login_required +from django.core.exceptions import ValidationError from django.http import HttpResponseNotAllowed, HttpResponseNotFound from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.template.context import RequestContext @@ -23,8 +24,11 @@ def add_link(request): link = form.save(commit=False) link.site = request.site link.user = request.user - link.save() - return redirect(get_all_links) + try: + link.save() + return redirect(get_all_links) + except ValidationError as ve: + form._update_errors(ve.message_dict) else: form = AddLinkForm() return render_to_response("links/add.html", RequestContext(request, {'form': form}))