Permalink
Browse files

DeletionMixin, BaseDeleteView and DeleteView added. View documentatio…

…n re-organized.
  • Loading branch information...
1 parent 1cb8be8 commit e14b572bda7c77955dd8e7be88ccc69be6d4f06c @brocaar committed Feb 26, 2012
View
@@ -4,7 +4,7 @@ Changelog
0.3 (in development)
--------------------
-* ...
+* Classes added for removing a single Mongoengine document.
0.2.1
@@ -4,17 +4,20 @@ Detail views
Views
-----
-``BaseDetailView``
-~~~~~~~~~~~~~~~~~~
+``DetailView``
+~~~~~~~~~~~~~~
-.. autoclass:: flask_views.db.mongoengine.detail.BaseDetailView
+.. autoclass:: flask_views.db.mongoengine.detail.DetailView
:members:
-``DetailView``
-~~~~~~~~~~~~~~
+Base views
+----------
-.. autoclass:: flask_views.db.mongoengine.detail.DetailView
+``BaseDetailView``
+~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: flask_views.db.mongoengine.detail.BaseDetailView
:members:
@@ -4,17 +4,34 @@ Edit views
Views
-----
-``BaseCreateView``
-~~~~~~~~~~~~~~~~~~
+``CreateView``
+~~~~~~~~~~~~~~
-.. autoclass:: flask_views.db.mongoengine.edit.BaseCreateView
+.. autoclass:: flask_views.db.mongoengine.edit.CreateView
:members:
-``CreateView``
+``UpdateView``
~~~~~~~~~~~~~~
-.. autoclass:: flask_views.db.mongoengine.edit.CreateView
+.. autoclass:: flask_views.db.mongoengine.edit.UpdateView
+ :members:
+
+
+``DeleteView``
+~~~~~~~~~~~~~~
+
+.. autoclass:: flask_views.db.mongoengine.edit.DeleteView
+ :members:
+
+
+Base views
+----------
+
+``BaseCreateView``
+~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: flask_views.db.mongoengine.edit.BaseCreateView
:members:
@@ -25,16 +42,23 @@ Views
:members:
-``UpdateView``
-~~~~~~~~~~~~~~
+``BaseDeleteView``
+~~~~~~~~~~~~~~~~~~
-.. autoclass:: flask_views.db.mongoengine.edit.UpdateView
+.. autoclass:: flask_views.db.mongoengine.edit.BaseDeleteView
:members:
Mixins
------
+``DeletionMixin``
+~~~~~~~~~~~~~~~~~
+
+.. autoclass:: flask_views.db.mongoengine.edit.DeletionMixin
+ :members:
+
+
``ModelFormMixin``
~~~~~~~~~~~~~~~~~~
View
@@ -4,19 +4,23 @@ Edit views
Views
-----
-``BaseFormView``
-~~~~~~~~~~~~~~~~
-
-.. autoclass:: flask_views.edit.BaseFormView
- :members:
-
``FormView``
~~~~~~~~~~~~
.. autoclass:: flask_views.edit.FormView
:members:
+Base views
+----------
+
+``BaseFormView``
+~~~~~~~~~~~~~~~~
+
+.. autoclass:: flask_views.edit.BaseFormView
+ :members:
+
+
Mixins
------
@@ -1,5 +1,7 @@
+from flask import redirect
+
from flask_views.base import TemplateResponseMixin, View
-from flask_views.db.mongoengine.detail import SingleObjectMixin
+from flask_views.db.mongoengine.detail import BaseDetailView, SingleObjectMixin
from flask_views.edit import FormMixin, ProcessFormMixin
@@ -182,3 +184,94 @@ def get_success_url(self):
about how the object is retrieved from the database.
"""
+
+
+class DeletionMixin(object):
+ """
+ Mixin class for deleting a single object.
+ """
+ success_url = None
+ """
+ Set this to the URL the user should be redirected to after a successful
+ deletion.
+ """
+
+ def get_success_url(self):
+ """
+ Return success URL.
+
+ Override this method when the success URL depends on the deleted object
+ or when it should be generated during an request. By default, this
+ returns
+ :py:data:`~flask_views.db.mongoengine.edit.DeletionMixin.success_url`.
+
+ :return:
+ :data:`~flask_views.db.mongoengine.edit.DeletionMixin.success_url`.
+
+ """
+ return self.success_url
+
+ def delete(self, *args, **kwargs):
+ """
+ Delete object and redirect user to configured success URL.
+
+ :return:
+ Redirect to URL returned by
+ :py:meth:`~.DeletionMixin.get_success_url`.
+
+ """
+ self.object.delete()
+ return redirect(self.get_success_url())
+
+
+class BaseDeleteView(DeletionMixin, BaseDetailView):
+ """
+ Base view class for deleting a single object.
+
+ This class inherits from:
+
+ * :py:class:`.DeletionMixin`
+ * :py:class:`.BaseDetailView`
+
+ This class implements all logic for deleting a single object from the
+ database, but does not implement rendering responses. See
+ :py:class:`.DetailView` for an usage example.
+
+ """
+ def delete(self, *args, **kwargs):
+ """
+ Handler for DELETE requests.
+
+ This retrieves the object from the database. Then it will call
+ :py:meth:`.DeletionMixin.delete`.
+
+ :return:
+ Return of :py:meth:`.DeletionMixin.delete`.
+
+ """
+ self.object = self.get_object()
+ return super(BaseDeleteView, self).delete(*args, **kwargs)
+
+
+class DeleteView(TemplateResponseMixin, BaseDeleteView):
+ """
+ View class for deleting a single object.
+
+ This class inherits from:
+
+ * :py:class:`.TemplateResponseMixin`
+ * :py:class:`.BaseDeleteView`
+
+ This class implements all logic for deleting a single object from the
+ database, including rendering a template.
+
+ Usage example::
+
+ class DeleteArticleView(DeleteView):
+ get_fields = {
+ 'id': 'id',
+ }
+ document_class = Article
+ template_name = 'article_delete.html'
+
+ """
@@ -1,6 +1,6 @@
from flask import url_for
-from flask_views.db.mongoengine.edit import CreateView, UpdateView
+from flask_views.db.mongoengine.edit import CreateView, UpdateView, DeleteView
from flask_views.tests.functional.db.mongoengine.base import BaseMongoTestCase
@@ -108,3 +108,50 @@ def test_post(self):
self.assertEqual(1, self.TestDocument.objects.count())
self.TestDocument.objects.get(username='john', name='Foo Bar')
+
+
+class DeleteViewTestCase(BaseMongoTestCase):
+ """
+ Tests for :py:class:`.DeleteView`.
+ """
+ def setUp(self):
+ super(DeleteViewTestCase, self).setUp()
+
+ class TestView(DeleteView):
+ document_class = self.TestDocument
+ template_name = 'detail_template.html'
+ success_url = 'http://google.com/'
+ get_fields = {
+ 'username': 'user',
+ }
+
+ self.app.add_url_rule('/<user>/', view_func=TestView.as_view('test'))
+
+ self.TestDocument(username='foo', name='bar').save()
+ self.TestDocument(username='bar', name='foo').save()
+
+ def test_get(self):
+ """
+ Test GET request.
+ """
+ with self.app.test_request_context():
+ response = self.client.get(url_for('test', user='foo'))
+ self.assertEqual(200, response.status_code)
+ self.assertEqual('bar', response.data)
+
+ def test_delete(self):
+ """
+ Test DELETE request.
+ """
+ self.assertEqual(2, self.TestDocument.objects.count())
+ with self.app.test_request_context():
+ response = self.client.delete(url_for('test', user='foo'))
+ self.assertEqual(302, response.status_code)
+ self.assertEqual('http://google.com/', response.headers['Location'])
+ self.assertEqual(1, self.TestDocument.objects.count())
+
+ self.TestDocument.objects.get(username='bar')
+ self.assertRaises(
+ self.TestDocument.DoesNotExist,
+ self.TestDocument.objects.get, username='foo'
+ )
@@ -5,7 +5,13 @@
from flask_views.base import View, TemplateResponseMixin
from flask_views.db.mongoengine.detail import SingleObjectMixin
from flask_views.db.mongoengine.edit import (
- ModelFormMixin, BaseCreateView, CreateView, BaseUpdateView, UpdateView
+ BaseCreateView,
+ BaseDeleteView,
+ BaseUpdateView,
+ CreateView,
+ DeletionMixin,
+ ModelFormMixin,
+ UpdateView,
)
from flask_views.edit import FormMixin, ProcessFormMixin
@@ -198,3 +204,52 @@ class UpdateViewTestCase(unittest.TestCase):
def test_inherited_classes(self):
for class_obj in [TemplateResponseMixin, BaseUpdateView]:
self.assertIn(class_obj, UpdateView.mro())
+
+
+class DeletionMixinTestCase(unittest.TestCase):
+ """
+ Tests for :py:class:`.DeletionMixin`.
+ """
+ def test_get_success_url(self):
+ """
+ Test :py:meth:`.DeletionMixin.get_success_url`.
+ """
+ mixin = DeletionMixin()
+ mixin.success_url = 'success-url'
+ self.assertEqual('success-url', mixin.get_success_url())
+
+ @patch('flask_views.db.mongoengine.edit.redirect')
+ def test_delete(self, redirect):
+ """
+ Test :py:meth:`.DeletionMixin.delete`.
+ """
+ mixin = DeletionMixin()
+ mixin.object = Mock()
+ mixin.get_success_url = Mock(return_value='success-url')
+
+ self.assertEqual(redirect.return_value, mixin.delete())
+ mixin.object.delete.assert_called_once_with()
+ redirect.assert_called_once_with('success-url')
+
+
+class BaseDeleteViewTestCase(unittest.TestCase):
+ """
+ Tests for :py:class:`.BaseDeleteView`.
+ """
+ @patch('flask_views.db.mongoengine.edit.super', create=True)
+ def test_delete(self, super_mock):
+ """
+ Test :py:meth:`.BaseDeleteView.delete`.
+ """
+ super_class = Mock()
+ super_mock.return_value = super_class
+
+ view = BaseDeleteView()
+ view.get_object = Mock()
+
+ self.assertEqual(
+ super_class.delete.return_value,
+ view.delete('foo', bar='foo')
+ )
+ self.assertEqual(view.get_object.return_value, view.object)
+ super_class.delete.assert_called_once_with('foo', bar='foo')

0 comments on commit e14b572

Please sign in to comment.