class View(MethodView):
    """
    View which will dispatch requests based on request method.

    This class inherits from:

    * :py:class:`!flask.views.MethodView`

    For example, a GET request will be dispatched to the ``get`` method, a POST
    request will be dispatched to the ``post`` method. Example usage::

        class MethodView(View):

            def get(self, *args, **kwargs):
                return 'Hello {0}'.format(self.kwargs.get('user'))

    When you have a URL route ``'/<user>/'`` and you GET ``/john/``, it
    will return ``'Hello john'``.

    """
    def dispatch_request(self, *args, **kwargs):
        """
        Dispatch the request based on HTTP method.

        This sets the arguments and keyword-arguments passed by the
        URL route dispatcher to ``self.args`` and ``self.kwargs``, then it will
        dispatch the request to the right method.

        """
        self.args = args
        self.kwargs = kwargs
        return super(View, self).dispatch_request(*args, **kwargs) class TemplateResponseMixin(object):
    """
    Mixin class for rendering templates.

    This will use the :py:meth:`!flask.render_template` method for rendering
    (Jinja2) templates.

    .. seealso:: http://jinja.pocoo.org/

    """

    template_name = None
    """
    Set this variable to the template you want to render.
    """

    def render_to_response(self, context_data={}):
        """
        Render template with the given context data.

        :param context_data:
            A ``dict`` containing the context data. Optional.

        :return:
            The rendered template as a string.

        """
        return render_template(self.template_name, **context_data) class SingleObjectMixin(object):
    """
    Mixin for retrieving a single object from the database.
    """
    document_class = None
    """
    Document class from which the object should be retrieved.

    .. seealso:: http://mongoengine.org/

    """

    get_fields = {
        'id': 'id',
    }
    """
    A ``dict`` containing the fieldname mapped against the URL variable name.
    For example when you have the URL route ``'/<category>/<author>/'``, and
    the following setting::

        get_fields = {
            'cat': 'category',
            'user': 'author',
        }

    When requesting ``/news/john/``, it would perform the following query:
    ``get(cat='news', user='john')``.

    """

    context_object_name = None
    """
    The variable name to give this object in the template context. If ``None``,
    it will get the name of the model class (Eg: a class ``Page`` would be
    available as ``page``).

    """
    def get_context_object_name(self):
        """
        Return the context object name.

        If :py:attr:`.SingleObjectMixin.context_object_name` is set, that value
        will be returned, else it will use the lowercased name of the model
        set in :py:attr:`.SingleObjectMixin.document_class`.

        :return:
            A ``str`` representing the object name.

        """
        if self.context_object_name:
            return self.context_object_name

        return self.document_class.__name__.lower()

    def get_queryset(self):
        """
        Return ``QuerySet`` class used to retrieve objects.

        :return:
            An instance of :py:class:`!mongoengine.queryset.QuerySet`.

        """
        return self.document_class.objects

    def get_object(self):
        """
        Retrieve the object from the database.

        :return:
            An instance of the model class containing the retrieved object.

        :raise:
            :py:exc:`!werkzeug.exceptions.NotFound` when the document does not
            exist.

        """
        lookup_args = {}

        for field_name, mapping_name in self.get_fields.items():
            lookup_args[field_name] = self.kwargs.get(mapping_name, None)

        try:
            return self.get_queryset().get(**lookup_args)
        except self.document_class.DoesNotExist:
            abort(404)

    def get_context_data(self, **kwargs):
        """
        Return context data containing the retrieved object.

        :return:
            A ``dict`` containing the retrieved object.

        """
        kwargs[self.get_context_object_name()] = self.object
        return kwargs class BaseCreateView(ModelFormMixin, ProcessFormMixin, View):
    """
    Base view for creating objects.

    This class inherits from:

    * :py:class:`.ModelFormMixin`
    * :py:class:`.ProcessFormMixin`
    * :py:class:`.View`

    This implements all logic for creating objects, but does not implement
    the rendering of responses. See :py:class:`.CreateView` for an usage
    example.

    """
    def get(self, *args, **kwargs):
        """
        Handler for GET requests.

        This will set ``self.object`` to ``None``.

        :return:
            Output returned by :py:meth:`.ProcessFormMixin.get`.

        """
        self.object = None
        return super(BaseCreateView, self).get(*args, **kwargs)

    def post(self, *args, **kwargs):
        """
        Handler for POST requests.

        This will set ``self.object`` to ``None``.

        :return:
            Output returned by :py:meth:`.ProcessFormMixin.post`.

        """
        self.object = None
        return super(BaseCreateView, self).post(*args, **kwargs) 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 BaseUpdateView(ModelFormMixin, ProcessFormMixin, View):
    """
    Base view for updating objects.

    This class inherits from:

    * :py:class:`.ModelFormMixin`
    * :py:class:`.ProcessFormMixin`

    This implements all logic for retrieving the object for updating and
    processing the form. This does not implement rendering the responses.
    See :py:class:`.UpdateView` for an usage example.

    """
    def get(self, *args, **kwargs):
        """
        Handler for GET requests.
        """
        self.object = self.get_object()
        return super(BaseUpdateView, self).get(*args, **kwargs)

    def post(self, *args, **kwargs):
        """
        Handler for POST requests.
        """
        self.object = self.get_object()
        return super(BaseUpdateView, self).post(*args, **kwargs) class CreateView(TemplateResponseMixin, BaseCreateView):
    """
    View for creating objects.

    This class inherits from:

    * :py:class:`.TemplateResponseMixin`
    * :py:class:`.BaseCreateView`

    This implements all logic for creating objects, including the rendering
    of a template. Usage example::

        class PostCreateView(CreateView):
            form_class = PostForm
            document_class = Post
            template_name = 'post_form.html'

            def get_success_url(self):
                return url_for('posts.index')

    """ 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 ModelFormMixin(FormMixin, SingleObjectMixin):
    """
    Mixin for handling model-form processing.

    This class inherits from:

    * :py:class:`.FormMixin`
    * :py:class:`.SingleObjectMixin`

    """
    def get_form_kwargs(self):
        """
        Return parameters for creating the form instance.

        This adds a model instance (or ``None``) to the form parameters
        returned by :py:meth:`.FormMixin.get_form_kwargs`.

        :return:
            A ``dict`` containing the arguments for creating the form instance.

        """
        kwargs = super(ModelFormMixin, self).get_form_kwargs()
        kwargs['obj'] = self.object
        return kwargs

    def form_valid(self, form):
        """
        Handle a valid form submission.

        When editing an object, the object will be updated and saved, else
        a new object will be created and saved.

        :return:
            Output returned by :py:meth:`.FormMixin.form_valid`.

        """
        if not self.object:
            self.object = self.document_class()
        form.populate_obj(self.object)
        self.object.save()
        return super(ModelFormMixin, self).form_valid(form)

    def get_context_data(self, **kwargs):
        """
        Return context data for rendering template.

        This adds ``self.object`` as ``object`` to the context data returned
        by :py:meth:`.FormMixin.get_context_data`.

        :return:
            A ``dict`` containing the context data.

        """
        context = super(ModelFormMixin, self).get_context_data(**kwargs)
        context.update({
            'object': self.object,
        })
        return context class UpdateView(TemplateResponseMixin, BaseUpdateView):
    """
    View for updating objects.

    This class inherits from:

    * :py:class:`.TemplateResponseMixin`
    * :py:class:`.BaseUpdateView`

    This implements all logic for retrieving the object for updating and
    processing the form. As well this implements the rendering of a template.
    Usage example::

        class PostUpdateView(UpdateView):
            form_class = PostForm
            document_class = Post
            template_name = 'post_form.html'

            def get_success_url(self):
                url_for('posts.index')

    .. seealso:: :py:attr:`.SingleObjectMixin.get_fields` for more information
        about how the object is retrieved from the database.

    """ class FormMixin(object):
    """
    Mixin for handling form submissions.
    """
    form_class = None
    """
    Set this to the form class (WTForms) you want to use.

    .. seealso:: http://wtforms.simplecodes.com/

    """

    initial = {}
    """
    Set this to the initial data for form fields. Example::

        initial = {
            'title': 'Initial title value',
            'body': 'Initial body value.',
        }

    """

    success_url = None
    """
    Set this to the URL the user should be redirected to after a successful
    form submission.
    """

    def get_initial(self):
        """
        Return initial form data.

        Override this method when the initial form data should be generated
        dynamically. By default this returns
        :py:data:`~flask_views.edit.FormMixin.initial`.

        :return:
            :py:data:`~flask_views.edit.FormMixin.initial`.

        """
        return self.initial

    def get_success_url(self):
        """
        Return success URL.

        Override this method when the success URL depends on the submitted
        form data or when it should be generated during an request. By default,
        this returns :py:data:`~flask_views.edit.FormMixin.success_url`.

        :return:
            :py:data:`~flask_views.edit.FormMixin.success_url`.

        """
        return self.success_url

    def get_context_data(self, **kwargs):
        """
        Return a ``dict`` containing the context data.

        :return:
            A ``dict`` containing the given keyword arguments.

        """
        return kwargs

    def get_form_kwargs(self):
        """
        Return parameters for creating the form instance.

        :return:
            A ``dict`` containing the arguments for creating the form instance.

        """
        kwargs = {'formdata': request.form}
        kwargs.update(self.get_initial())
        return kwargs

    def get_form(self):
        """
        Return an instance of the form class.

        :return:
            Instance :py:class:`~flask_views.edit.FormMixin.form_class`.

        """
        return self.form_class(**self.get_form_kwargs())

    def form_valid(self, form):
        """
        Handle valid form submission.

        This redirects the user to the URL returned by
        :py:meth:`~flask_views.edit.FormMixin.get_success_url`. You want to
        override this method for processing the submitted form data.

        :param form:
            Instance of the form.

        :return:
            Redirect to URL returned by
            :py:meth:`~flask_views.edit.FormMixin.get_success_url`.

        """
        return redirect(self.get_success_url())

    def form_invalid(self, form):
        """
        Handle invalid form submission.

        This will render the response with the current ``form`` in the context
        so that the errors can be displayed to the user.

        :param form:
            Instance of the form class.

        :return:
            Response containing the ``form`` in the context.

        """
        return self.render_to_response(self.get_context_data(form=form)) class ProcessFormMixin(object):
    """
    Mixin for processing form data on GET and POST requests.
    """
    methods = ['GET', 'POST']

    def get(self, *args, **kwargs):
        """
        Handler for GET requests.

        This will call ``render_to_response`` with an instance of the form
        as ``form`` in the context data.

        :return:
            Output of ``render_to_response`` method implementation.

        """
        form = self.get_form()
        return self.render_to_response(self.get_context_data(form=form))

    def post(self, *args, **kwargs):
        """
        Handler for POST requests.

        On a valid form submission, this will dispatch the request to
        the ``form_valid`` method, else it is dispatched to ``form_invalid``.

        :return:
            Output of ``form_valid`` or ``form_invalid``.

        """
        form = self.get_form()
        if form.validate():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)
import unittest2 as unittest
from mock import Mock, patch
from flask_views.base import View, TemplateResponseMixin
from flask_views.db.mongoengine.detail import SingleObjectMixin
from flask_views.db.mongoengine.edit import (
    BaseCreateView,
    BaseDeleteView,
    BaseUpdateView,
    CreateView,
    DeletionMixin,
    ModelFormMixin,
    UpdateView,
)
from flask_views.edit import FormMixin, ProcessFormMixin

class BaseCreateViewTestCase(unittest.TestCase):
    """
    Tests for :py:class:`.BaseCreateView`.
    """
    def test_inherited_classes(self):
        """
        Test that this class inherits from the right classes.
        """
        for class_obj in [ModelFormMixin, ProcessFormMixin, View]:
            self.assertIn(class_obj, BaseCreateView.mro())

    @patch('flask_views.db.mongoengine.edit.super', create=True)
    def test_get(self, super_mock):
        """
        Test :py:meth:`.BaseCreateView.get`.
        """
        super_class = Mock()
        super_class.get.return_value = 'get-response'
        super_mock.return_value = super_class

        view = BaseCreateView()
        view.object = 'foo'

        self.assertEqual('get-response', view.get('something', foo='bar'))
        self.assertEqual(None, view.object)
        super_class.get.assert_called_once_with('something', foo='bar')

    @patch('flask_views.db.mongoengine.edit.super', create=True)
    def test_post(self, super_mock):
        """
        Test :py:meth:`.BaseCreateView.post`.
        """
        super_class = Mock()
        super_class.post.return_value = 'post-response'
        super_mock.return_value = super_class

        view = BaseCreateView()
        view.object = 'foo'

        self.assertEqual('post-response', view.post('something', foo='bar'))
        self.assertEqual(None, view.object)
        super_class.post.assert_called_once_with('something', foo='bar')


class CreateViewTestCase(unittest.TestCase):
    """
    Tests for :py:class:`.CreateView`.
    """
    def test_inherited_classes(self):
        """
        Test that this class inherits from the right classes.
        """
        for class_obj in [TemplateResponseMixin, BaseCreateView]:
            self.assertIn(class_obj, CreateView.mro())


class BaseUpdateViewTestCase(unittest.TestCase):
    """
    Tests for :py:class:`.BaseUpdateView`.
    """
    @patch('flask_views.db.mongoengine.edit.super', create=True)
    def test_get(self, super_mock):
        """
        Test :py:meth:`.BaseUpdateView.get`.
        """
        super_class = Mock()
        super_class.get.return_value = 'get-response'
        super_mock.return_value = super_class

        view = BaseUpdateView()
        view.get_object = Mock(return_value='object')

        self.assertEqual('get-response', view.get('someting', foo='bar'))
        self.assertEqual('object', view.object)
        super_class.get.assert_called_once_with('someting', foo='bar')

    @patch('flask_views.db.mongoengine.edit.super', create=True)
    def test_post(self, super_mock):
        """
        Test :py:meth:`.BaseUpdateView.post`.
        """
        super_class = Mock()
        super_class.post.return_value = 'post-response'
        super_mock.return_value = super_class

        view = BaseUpdateView()
        view.get_object = Mock(return_value='object')

        self.assertEqual('post-response', view.post('something', foo='bar'))
        self.assertEqual('object', view.object)
        super_class.post.assert_called_once_with('something', foo='bar')


class UpdateViewTestCase(unittest.TestCase):
    """
    Tests for :py:class:`.UpdateView`.
    """
    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()