Permalink
Browse files

confirm and unsubscribe links now redirect to the product page

* adds new RedirectView for unsubscribe and confirm views
* corresponding tests are using WebTest now
* notification URL patterns use the same named keywords as the
    catalogue URL patterns to provide generic access to the absolute
    URL for a notification item.
    This required the notification URL patterns with PKs for
    notifications to be changed. They now use ``notification_pk``
    instead of ``pk`` to prevent conflicts with product ``pk`` keywords.
* The ``AbstractNotification`` model now has a ``get_absolute_item_url``
    that provides the URL for the item referenced by a notification.
    This required a new attribute on the notification class called
    ``item_url_index``, e.g. ``catalogue:detail`` for product
    notifications.
  • Loading branch information...
1 parent 9024a98 commit e214bc04b6178c994a5b2f5ff99835ba9eff8560 Sebastian Vetter committed Sep 5, 2012
@@ -44,7 +44,7 @@ class NotificationsApplication(Application):
def get_urls(self):
urlpatterns = super(NotificationsApplication, self).get_urls()
urlpatterns += patterns('',
- url(r'^(?P<product_slug>[\w-]*)-(?P<product_pk>\d+)/notify-me/',
+ url(r'^(?P<product_slug>[\w-]*)_(?P<pk>\d+)/notify-me/',
include(self.notification_app.urls)),
)
return self.post_process_urls(urlpatterns)
@@ -23,6 +23,7 @@ class AbstractNotification(models.Model):
"""
KEY_LENGTH = 40
item_field_name = None
+ item_url_index = None
user = models.ForeignKey(User, db_index=True, blank=True, null=True,
related_name="notifications")
@@ -102,7 +103,7 @@ def get_confirm_url(self):
and ``pk`` fields prefixed with the specified ``item_field_name``,
e.g. for a product notification with ``item_field_name='product'``
the URL reverse lookup requires ``product_slug`` and
- ``product_pk`` as part of its URL pattern definition.
+ ``pk`` as part of its URL pattern definition.
"""
kwargs = self._get_url_kwargs()
kwargs['key'] = self.confirm_key
@@ -116,12 +117,22 @@ def get_unsubscribe_url(self):
and ``pk`` fields prefixed with the specified ``item_field_name``,
e.g. for a product notification with ``item_field_name='product'``
the URL reverse lookup requires ``product_slug`` and
- ``product_pk`` as part of its URL pattern definition.
+ ``pk`` as part of its URL pattern definition.
"""
kwargs = self._get_url_kwargs()
kwargs['key'] = self.unsubscribe_key
return ('catalogue:notification-unsubscribe', (), kwargs)
+ @models.permalink
+ def get_absolute_item_url(self):
+ """
+ Get the absolute URL for the item referenced by this
+ notification. The URL patterns uses the ``item_url_index``
+ attribute specified in the class definition.
+ """
+ kwargs = self._get_url_kwargs()
+ return (self.item_url_index, (), kwargs)
+
def save(self, *args, **kwargs):
"""
Save the current notification instance. If the user is not
@@ -155,11 +166,10 @@ def _get_url_kwargs(self):
and primary key.
"""
slug_key = '%s_slug' % self.item_field_name
- pk_key = '%s_pk' % self.item_field_name
item = self.get_notification_item()
return {
slug_key: item.slug,
- pk_key: item.id,
+ 'pk': item.id,
}
def __unicode__(self):
@@ -25,12 +25,12 @@ def get_urls(self):
name='notification-create'),
# Make sure that only valid status values are allowed in the
# URL pattern
- url(r'^(?P<pk>\d+)/set-status/(?P<status>%s)/$' % (
+ url(r'^(?P<notification_pk>\d+)/set-status/(?P<status>%s)/$' % (
'|'.join([x[0] for x in AbstractNotification.STATUS_TYPES])
),
login_required(self.update_view.as_view()),
name='notification-set-status'),
- url(r'^(?P<pk>\d+)/delete/$',
+ url(r'^(?P<notification_pk>\d+)/delete/$',
login_required(self.delete_view.as_view()),
name='notification-delete'),
)
@@ -14,7 +14,8 @@ class ProductNotification(Notification):
A product notification that is used to notify the set user when the
specified product is back in stock.
"""
- item_field_name = 'product'
+ item_field_name = "product"
+ item_url_index = "catalogue:detail"
product = models.ForeignKey(Product, db_index=True)
@@ -1,6 +1,6 @@
from django.conf import settings
from django.views import generic
-from django.http import HttpResponseRedirect
+from django.http import HttpResponseRedirect, Http404
from django.core import mail
from django.contrib import messages
@@ -19,21 +19,23 @@
ProductNotification = get_model('notification', 'productnotification')
-class NotificationUnsubscribeView(generic.DetailView):
+class NotificationUnsubscribeView(generic.RedirectView):
"""
View to unsubscribe from a notification based on the provided
unsubscribe key. The notification is set to ``INACTIVE`` instead
of being deleted for analytical purposes.
"""
model = Notification
context_object_name = 'notification'
- template_name = 'notification/unsubscribe.html'
def get_object(self, queryset=None):
""" Get notification object that matches the unsubscribe key. """
- return get_object_or_404(self.model,
- unsubscribe_key=self.kwargs.get('key',
- 'invalid'))
+ try:
+ return self.model.objects.select_subclasses().get(
+ unsubscribe_key=self.kwargs.get('key', 'invalid')
+ )
+ except self.model.DoesNotExist:
+ raise Http404
def get(self, *args, **kwargs):
notification = self.get_object()
@@ -42,22 +44,29 @@ def get(self, *args, **kwargs):
messages.info(self.request,
_("You have successfully unsubscribed from this notification.")
)
+ kwargs['notification'] = notification
return super(NotificationUnsubscribeView, self).get(*args, **kwargs)
+ def get_redirect_url(self, **kwargs):
+ return kwargs.get('notification').get_absolute_item_url()
+
-class NotificationConfirmView(generic.DetailView):
+class NotificationConfirmView(generic.RedirectView):
"""
View to confirm the email address of an anonymous user used to
sign up for a product notification.
"""
model = Notification
context_object_name = 'notification'
- template_name = 'notification/confirm.html'
def get_object(self, queryset=None):
""" Get notification object that matches the confirmation key. """
- return get_object_or_404(self.model,
- confirm_key=self.kwargs.get('key', 'invalid'))
+ try:
+ return self.model.objects.select_subclasses().get(
+ confirm_key=self.kwargs.get('key', 'invalid')
+ )
+ except self.model.DoesNotExist:
+ raise Http404
def get(self, *args, **kwargs):
notification = self.get_object()
@@ -67,8 +76,12 @@ def get(self, *args, **kwargs):
_("Yeah! You have confirmed your subscription. We'll notify "
"you as soon as the product is back in stock.")
)
+ kwargs['notification'] = notification
return super(NotificationConfirmView, self).get(*args, **kwargs)
+ def get_redirect_url(self, **kwargs):
+ return kwargs.get('notification').get_absolute_item_url()
+
class ProductNotificationCreateView(generic.FormView):
"""
@@ -97,10 +110,9 @@ def get_form_kwargs(self):
def get_product(self):
"""
Get product from primary key specified in URL patterns as
- ``product_pk``. Raises a 404 if product does not exist.
+ ``pk``. Raises a 404 if product does not exist.
"""
- return get_object_or_404(self.product_model,
- pk=self.kwargs['product_pk'])
+ return get_object_or_404(self.product_model, pk=self.kwargs['pk'])
def get_context_data(self, *args, **kwargs):
"""
@@ -233,8 +245,10 @@ def get_success_url(self):
"""
Get success URL to redirect to the product's detail page.
"""
- return reverse('catalogue:detail',
- args=(self.product.slug, self.product.pk))
+ return reverse('catalogue:detail', kwargs={
+ 'product_slug':self.product.slug,
+ 'pk': self.product.pk
+ })
class ProductNotificationSetStatusView(generic.TemplateView):
@@ -244,6 +258,7 @@ class ProductNotificationSetStatusView(generic.TemplateView):
"""
model = ProductNotification
status_types = [map[0] for map in ProductNotification.STATUS_TYPES]
+ pk_url_kwarg = 'notification_pk'
def get(self, *args, **kwargs):
"""
@@ -252,7 +267,7 @@ def get(self, *args, **kwargs):
or ``inactive`` a HTTP redirect is returned without changing the
notification. Otherwise the notification status is updated
"""
- self.product = get_object_or_404(Product, pk=kwargs.get('product_pk'))
+ self.product = get_object_or_404(Product, pk=kwargs.get('pk'))
status = kwargs.get('status', None)
if status in (self.status_types):
@@ -289,6 +304,7 @@ class ProductNotificationDeleteView(generic.DeleteView):
"""
model = ProductNotification
template_name = 'notification/delete.html'
+ pk_url_kwarg = 'notification_pk'
def get_success_url(self):
return reverse('dashboard:index')
@@ -1,14 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block main-column %}
- {% if notification.is_active %}
- <div class="alert alert-success">
- {% blocktrans with notification_email=notification.email %}Thank you for subscribing. Notifications will be sent to {{ notification_email }}{% endblocktrans %}
- </div>
- {% else %}
- <div class="alert alert-error">
- {% blocktrans %}Something went wrong confirming your email address. Please try again or contact us.{% endblocktrans %}
- </div>
- {% endif %}
-{% endblock %}
@@ -1,11 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-
-{% block main-column %}
- <div class="alert alert-success">
- {% blocktrans %}
- You have successfully unsubscribed from this notification.
- We are sorry to see you go and hope to see you back here soon.
- {% endblocktrans %}
- </div>
-{% endblock %}
Oops, something went wrong.

0 comments on commit e214bc0

Please sign in to comment.