We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Download options in Datatables_view do not work
**CKAN version**
2.9

**Describe the bug**
Using datatables_view as a default resource view, which works well.  Apart from the nicer UI and pagination, one benefit of the view is that you can download a filtered version of the resource (https://github.com/ckan/ckan/pull/4497).  However, none of the datatables_view download buttons work to download the filtered data.

**Steps to reproduce**

1. Add a CSV resource to a dataset
2. Create a datatables resource view (labeled 'Table' in the resource view picklist)
3. Go to resource view and try to use the Download button for any format type
4. A 404 error page replaces the datatables control



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ckanext/datatablesview/blueprint.py`
Content:
```
1 # encoding: utf-8
2 
3 from six.moves.urllib.parse import urlencode
4 
5 from flask import Blueprint
6 from six import text_type
7 
8 from ckan.common import json
9 from ckan.plugins.toolkit import get_action, request, h
10 
11 datatablesview = Blueprint(u'datatablesview', __name__)
12 
13 
14 def merge_filters(view_filters, user_filters_str):
15     u'''
16     view filters are built as part of the view, user filters
17     are selected by the user interacting with the view. Any filters
18     selected by user may only tighten filters set in the view,
19     others are ignored.
20 
21     >>> merge_filters({
22     ...    u'Department': [u'BTDT'], u'OnTime_Status': [u'ONTIME']},
23     ...    u'CASE_STATUS:Open|CASE_STATUS:Closed|Department:INFO')
24     {u'Department': [u'BTDT'],
25      u'OnTime_Status': [u'ONTIME'],
26      u'CASE_STATUS': [u'Open', u'Closed']}
27     '''
28     filters = dict(view_filters)
29     if not user_filters_str:
30         return filters
31     user_filters = {}
32     for k_v in user_filters_str.split(u'|'):
33         k, sep, v = k_v.partition(u':')
34         if k not in view_filters or v in view_filters[k]:
35             user_filters.setdefault(k, []).append(v)
36     for k in user_filters:
37         filters[k] = user_filters[k]
38     return filters
39 
40 
41 def ajax(resource_view_id):
42     resource_view = get_action(u'resource_view_show'
43                                )(None, {
44                                    u'id': resource_view_id
45                                })
46 
47     draw = int(request.form[u'draw'])
48     search_text = text_type(request.form[u'search[value]'])
49     offset = int(request.form[u'start'])
50     limit = int(request.form[u'length'])
51     view_filters = resource_view.get(u'filters', {})
52     user_filters = text_type(request.form[u'filters'])
53     filters = merge_filters(view_filters, user_filters)
54 
55     datastore_search = get_action(u'datastore_search')
56     unfiltered_response = datastore_search(
57         None, {
58             u"resource_id": resource_view[u'resource_id'],
59             u"limit": 0,
60             u"filters": view_filters,
61         }
62     )
63 
64     cols = [f[u'id'] for f in unfiltered_response[u'fields']]
65     if u'show_fields' in resource_view:
66         cols = [c for c in cols if c in resource_view[u'show_fields']]
67 
68     sort_list = []
69     i = 0
70     while True:
71         if u'order[%d][column]' % i not in request.form:
72             break
73         sort_by_num = int(request.form[u'order[%d][column]' % i])
74         sort_order = (
75             u'desc' if request.form[u'order[%d][dir]' %
76                                     i] == u'desc' else u'asc'
77         )
78         sort_list.append(cols[sort_by_num] + u' ' + sort_order)
79         i += 1
80 
81     response = datastore_search(
82         None, {
83             u"q": search_text,
84             u"resource_id": resource_view[u'resource_id'],
85             u"offset": offset,
86             u"limit": limit,
87             u"sort": u', '.join(sort_list),
88             u"filters": filters,
89         }
90     )
91 
92     return json.dumps({
93         u'draw': draw,
94         u'iTotalRecords': unfiltered_response.get(u'total', 0),
95         u'iTotalDisplayRecords': response.get(u'total', 0),
96         u'aaData': [[text_type(row.get(colname, u''))
97                      for colname in cols]
98                     for row in response[u'records']],
99     })
100 
101 
102 def filtered_download(resource_view_id):
103     params = json.loads(request.params[u'params'])
104     resource_view = get_action(u'resource_view_show'
105                                )(None, {
106                                    u'id': resource_view_id
107                                })
108 
109     search_text = text_type(params[u'search'][u'value'])
110     view_filters = resource_view.get(u'filters', {})
111     user_filters = text_type(params[u'filters'])
112     filters = merge_filters(view_filters, user_filters)
113 
114     datastore_search = get_action(u'datastore_search')
115     unfiltered_response = datastore_search(
116         None, {
117             u"resource_id": resource_view[u'resource_id'],
118             u"limit": 0,
119             u"filters": view_filters,
120         }
121     )
122 
123     cols = [f[u'id'] for f in unfiltered_response[u'fields']]
124     if u'show_fields' in resource_view:
125         cols = [c for c in cols if c in resource_view[u'show_fields']]
126 
127     sort_list = []
128     for order in params[u'order']:
129         sort_by_num = int(order[u'column'])
130         sort_order = (u'desc' if order[u'dir'] == u'desc' else u'asc')
131         sort_list.append(cols[sort_by_num] + u' ' + sort_order)
132 
133     cols = [c for (c, v) in zip(cols, params[u'visible']) if v]
134 
135     h.redirect_to(
136         h.
137         url_for(u'datastore.dump', resource_id=resource_view[u'resource_id']) +
138         u'?' + urlencode({
139             u'q': search_text,
140             u'sort': u','.join(sort_list),
141             u'filters': json.dumps(filters),
142             u'format': request.params[u'format'],
143             u'fields': u','.join(cols),
144         })
145     )
146 
147 
148 datatablesview.add_url_rule(
149     u'/datatables/ajax/<resource_view_id>', view_func=ajax, methods=[u'POST']
150 )
151 
152 datatablesview.add_url_rule(
153     u'/datatables/filtered-download/<resource_view_id>',
154     view_func=filtered_download
155 )
156
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ckanext/datatablesview/blueprint.py b/ckanext/datatablesview/blueprint.py
--- a/ckanext/datatablesview/blueprint.py
+++ b/ckanext/datatablesview/blueprint.py
@@ -100,7 +100,7 @@
 
 
 def filtered_download(resource_view_id):
-    params = json.loads(request.params[u'params'])
+    params = json.loads(request.form[u'params'])
     resource_view = get_action(u'resource_view_show'
                                )(None, {
                                    u'id': resource_view_id
@@ -132,14 +132,14 @@
 
     cols = [c for (c, v) in zip(cols, params[u'visible']) if v]
 
-    h.redirect_to(
+    return h.redirect_to(
         h.
         url_for(u'datastore.dump', resource_id=resource_view[u'resource_id']) +
         u'?' + urlencode({
             u'q': search_text,
             u'sort': u','.join(sort_list),
             u'filters': json.dumps(filters),
-            u'format': request.params[u'format'],
+            u'format': request.form[u'format'],
             u'fields': u','.join(cols),
         })
     )
@@ -151,5 +151,5 @@
 
 datatablesview.add_url_rule(
     u'/datatables/filtered-download/<resource_view_id>',
-    view_func=filtered_download
+    view_func=filtered_download, methods=[u'POST']
 )

{"golden_diff": "diff --git a/ckanext/datatablesview/blueprint.py b/ckanext/datatablesview/blueprint.py\n--- a/ckanext/datatablesview/blueprint.py\n+++ b/ckanext/datatablesview/blueprint.py\n@@ -100,7 +100,7 @@\n \n \n def filtered_download(resource_view_id):\n-    params = json.loads(request.params[u'params'])\n+    params = json.loads(request.form[u'params'])\n     resource_view = get_action(u'resource_view_show'\n                                )(None, {\n                                    u'id': resource_view_id\n@@ -132,14 +132,14 @@\n \n     cols = [c for (c, v) in zip(cols, params[u'visible']) if v]\n \n-    h.redirect_to(\n+    return h.redirect_to(\n         h.\n         url_for(u'datastore.dump', resource_id=resource_view[u'resource_id']) +\n         u'?' + urlencode({\n             u'q': search_text,\n             u'sort': u','.join(sort_list),\n             u'filters': json.dumps(filters),\n-            u'format': request.params[u'format'],\n+            u'format': request.form[u'format'],\n             u'fields': u','.join(cols),\n         })\n     )\n@@ -151,5 +151,5 @@\n \n datatablesview.add_url_rule(\n     u'/datatables/filtered-download/<resource_view_id>',\n-    view_func=filtered_download\n+    view_func=filtered_download, methods=[u'POST']\n )\n", "issue": "Download options in Datatables_view do not work\n**CKAN version**\r\n2.9\r\n\r\n**Describe the bug**\r\nUsing datatables_view as a default resource view, which works well.  Apart from the nicer UI and pagination, one benefit of the view is that you can download a filtered version of the resource (https://github.com/ckan/ckan/pull/4497).  However, none of the datatables_view download buttons work to download the filtered data.\r\n\r\n**Steps to reproduce**\r\n\r\n1. Add a CSV resource to a dataset\r\n2. Create a datatables resource view (labeled 'Table' in the resource view picklist)\r\n3. Go to resource view and try to use the Download button for any format type\r\n4. A 404 error page replaces the datatables control\r\n\r\n\n", "before_files": [{"content": "# encoding: utf-8\n\nfrom six.moves.urllib.parse import urlencode\n\nfrom flask import Blueprint\nfrom six import text_type\n\nfrom ckan.common import json\nfrom ckan.plugins.toolkit import get_action, request, h\n\ndatatablesview = Blueprint(u'datatablesview', __name__)\n\n\ndef merge_filters(view_filters, user_filters_str):\n    u'''\n    view filters are built as part of the view, user filters\n    are selected by the user interacting with the view. Any filters\n    selected by user may only tighten filters set in the view,\n    others are ignored.\n\n    >>> merge_filters({\n    ...    u'Department': [u'BTDT'], u'OnTime_Status': [u'ONTIME']},\n    ...    u'CASE_STATUS:Open|CASE_STATUS:Closed|Department:INFO')\n    {u'Department': [u'BTDT'],\n     u'OnTime_Status': [u'ONTIME'],\n     u'CASE_STATUS': [u'Open', u'Closed']}\n    '''\n    filters = dict(view_filters)\n    if not user_filters_str:\n        return filters\n    user_filters = {}\n    for k_v in user_filters_str.split(u'|'):\n        k, sep, v = k_v.partition(u':')\n        if k not in view_filters or v in view_filters[k]:\n            user_filters.setdefault(k, []).append(v)\n    for k in user_filters:\n        filters[k] = user_filters[k]\n    return filters\n\n\ndef ajax(resource_view_id):\n    resource_view = get_action(u'resource_view_show'\n                               )(None, {\n                                   u'id': resource_view_id\n                               })\n\n    draw = int(request.form[u'draw'])\n    search_text = text_type(request.form[u'search[value]'])\n    offset = int(request.form[u'start'])\n    limit = int(request.form[u'length'])\n    view_filters = resource_view.get(u'filters', {})\n    user_filters = text_type(request.form[u'filters'])\n    filters = merge_filters(view_filters, user_filters)\n\n    datastore_search = get_action(u'datastore_search')\n    unfiltered_response = datastore_search(\n        None, {\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"limit\": 0,\n            u\"filters\": view_filters,\n        }\n    )\n\n    cols = [f[u'id'] for f in unfiltered_response[u'fields']]\n    if u'show_fields' in resource_view:\n        cols = [c for c in cols if c in resource_view[u'show_fields']]\n\n    sort_list = []\n    i = 0\n    while True:\n        if u'order[%d][column]' % i not in request.form:\n            break\n        sort_by_num = int(request.form[u'order[%d][column]' % i])\n        sort_order = (\n            u'desc' if request.form[u'order[%d][dir]' %\n                                    i] == u'desc' else u'asc'\n        )\n        sort_list.append(cols[sort_by_num] + u' ' + sort_order)\n        i += 1\n\n    response = datastore_search(\n        None, {\n            u\"q\": search_text,\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"offset\": offset,\n            u\"limit\": limit,\n            u\"sort\": u', '.join(sort_list),\n            u\"filters\": filters,\n        }\n    )\n\n    return json.dumps({\n        u'draw': draw,\n        u'iTotalRecords': unfiltered_response.get(u'total', 0),\n        u'iTotalDisplayRecords': response.get(u'total', 0),\n        u'aaData': [[text_type(row.get(colname, u''))\n                     for colname in cols]\n                    for row in response[u'records']],\n    })\n\n\ndef filtered_download(resource_view_id):\n    params = json.loads(request.params[u'params'])\n    resource_view = get_action(u'resource_view_show'\n                               )(None, {\n                                   u'id': resource_view_id\n                               })\n\n    search_text = text_type(params[u'search'][u'value'])\n    view_filters = resource_view.get(u'filters', {})\n    user_filters = text_type(params[u'filters'])\n    filters = merge_filters(view_filters, user_filters)\n\n    datastore_search = get_action(u'datastore_search')\n    unfiltered_response = datastore_search(\n        None, {\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"limit\": 0,\n            u\"filters\": view_filters,\n        }\n    )\n\n    cols = [f[u'id'] for f in unfiltered_response[u'fields']]\n    if u'show_fields' in resource_view:\n        cols = [c for c in cols if c in resource_view[u'show_fields']]\n\n    sort_list = []\n    for order in params[u'order']:\n        sort_by_num = int(order[u'column'])\n        sort_order = (u'desc' if order[u'dir'] == u'desc' else u'asc')\n        sort_list.append(cols[sort_by_num] + u' ' + sort_order)\n\n    cols = [c for (c, v) in zip(cols, params[u'visible']) if v]\n\n    h.redirect_to(\n        h.\n        url_for(u'datastore.dump', resource_id=resource_view[u'resource_id']) +\n        u'?' + urlencode({\n            u'q': search_text,\n            u'sort': u','.join(sort_list),\n            u'filters': json.dumps(filters),\n            u'format': request.params[u'format'],\n            u'fields': u','.join(cols),\n        })\n    )\n\n\ndatatablesview.add_url_rule(\n    u'/datatables/ajax/<resource_view_id>', view_func=ajax, methods=[u'POST']\n)\n\ndatatablesview.add_url_rule(\n    u'/datatables/filtered-download/<resource_view_id>',\n    view_func=filtered_download\n)\n", "path": "ckanext/datatablesview/blueprint.py"}], "after_files": [{"content": "# encoding: utf-8\n\nfrom six.moves.urllib.parse import urlencode\n\nfrom flask import Blueprint\nfrom six import text_type\n\nfrom ckan.common import json\nfrom ckan.plugins.toolkit import get_action, request, h\n\ndatatablesview = Blueprint(u'datatablesview', __name__)\n\n\ndef merge_filters(view_filters, user_filters_str):\n    u'''\n    view filters are built as part of the view, user filters\n    are selected by the user interacting with the view. Any filters\n    selected by user may only tighten filters set in the view,\n    others are ignored.\n\n    >>> merge_filters({\n    ...    u'Department': [u'BTDT'], u'OnTime_Status': [u'ONTIME']},\n    ...    u'CASE_STATUS:Open|CASE_STATUS:Closed|Department:INFO')\n    {u'Department': [u'BTDT'],\n     u'OnTime_Status': [u'ONTIME'],\n     u'CASE_STATUS': [u'Open', u'Closed']}\n    '''\n    filters = dict(view_filters)\n    if not user_filters_str:\n        return filters\n    user_filters = {}\n    for k_v in user_filters_str.split(u'|'):\n        k, sep, v = k_v.partition(u':')\n        if k not in view_filters or v in view_filters[k]:\n            user_filters.setdefault(k, []).append(v)\n    for k in user_filters:\n        filters[k] = user_filters[k]\n    return filters\n\n\ndef ajax(resource_view_id):\n    resource_view = get_action(u'resource_view_show'\n                               )(None, {\n                                   u'id': resource_view_id\n                               })\n\n    draw = int(request.form[u'draw'])\n    search_text = text_type(request.form[u'search[value]'])\n    offset = int(request.form[u'start'])\n    limit = int(request.form[u'length'])\n    view_filters = resource_view.get(u'filters', {})\n    user_filters = text_type(request.form[u'filters'])\n    filters = merge_filters(view_filters, user_filters)\n\n    datastore_search = get_action(u'datastore_search')\n    unfiltered_response = datastore_search(\n        None, {\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"limit\": 0,\n            u\"filters\": view_filters,\n        }\n    )\n\n    cols = [f[u'id'] for f in unfiltered_response[u'fields']]\n    if u'show_fields' in resource_view:\n        cols = [c for c in cols if c in resource_view[u'show_fields']]\n\n    sort_list = []\n    i = 0\n    while True:\n        if u'order[%d][column]' % i not in request.form:\n            break\n        sort_by_num = int(request.form[u'order[%d][column]' % i])\n        sort_order = (\n            u'desc' if request.form[u'order[%d][dir]' %\n                                    i] == u'desc' else u'asc'\n        )\n        sort_list.append(cols[sort_by_num] + u' ' + sort_order)\n        i += 1\n\n    response = datastore_search(\n        None, {\n            u\"q\": search_text,\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"offset\": offset,\n            u\"limit\": limit,\n            u\"sort\": u', '.join(sort_list),\n            u\"filters\": filters,\n        }\n    )\n\n    return json.dumps({\n        u'draw': draw,\n        u'iTotalRecords': unfiltered_response.get(u'total', 0),\n        u'iTotalDisplayRecords': response.get(u'total', 0),\n        u'aaData': [[text_type(row.get(colname, u''))\n                     for colname in cols]\n                    for row in response[u'records']],\n    })\n\n\ndef filtered_download(resource_view_id):\n    params = json.loads(request.form[u'params'])\n    resource_view = get_action(u'resource_view_show'\n                               )(None, {\n                                   u'id': resource_view_id\n                               })\n\n    search_text = text_type(params[u'search'][u'value'])\n    view_filters = resource_view.get(u'filters', {})\n    user_filters = text_type(params[u'filters'])\n    filters = merge_filters(view_filters, user_filters)\n\n    datastore_search = get_action(u'datastore_search')\n    unfiltered_response = datastore_search(\n        None, {\n            u\"resource_id\": resource_view[u'resource_id'],\n            u\"limit\": 0,\n            u\"filters\": view_filters,\n        }\n    )\n\n    cols = [f[u'id'] for f in unfiltered_response[u'fields']]\n    if u'show_fields' in resource_view:\n        cols = [c for c in cols if c in resource_view[u'show_fields']]\n\n    sort_list = []\n    for order in params[u'order']:\n        sort_by_num = int(order[u'column'])\n        sort_order = (u'desc' if order[u'dir'] == u'desc' else u'asc')\n        sort_list.append(cols[sort_by_num] + u' ' + sort_order)\n\n    cols = [c for (c, v) in zip(cols, params[u'visible']) if v]\n\n    return h.redirect_to(\n        h.\n        url_for(u'datastore.dump', resource_id=resource_view[u'resource_id']) +\n        u'?' + urlencode({\n            u'q': search_text,\n            u'sort': u','.join(sort_list),\n            u'filters': json.dumps(filters),\n            u'format': request.form[u'format'],\n            u'fields': u','.join(cols),\n        })\n    )\n\n\ndatatablesview.add_url_rule(\n    u'/datatables/ajax/<resource_view_id>', view_func=ajax, methods=[u'POST']\n)\n\ndatatablesview.add_url_rule(\n    u'/datatables/filtered-download/<resource_view_id>',\n    view_func=filtered_download, methods=[u'POST']\n)\n", "path": "ckanext/datatablesview/blueprint.py"}]}