Commit 7329f8aa authored by Serge S. Koval's avatar Serge S. Koval

Get rid of url_for in Flask-Admin templates and widgets and provide get_url...

Get rid of url_for in Flask-Admin templates and widgets and provide get_url helper that's overridable
parent 70a06219
from flask import request, url_for, redirect from flask import request, redirect
from flask.ext.admin import tools from flask.ext.admin import tools
...@@ -114,8 +114,8 @@ class ActionsMixin(object): ...@@ -114,8 +114,8 @@ class ActionsMixin(object):
return response return response
if not return_view: if not return_view:
url = url_for('.' + self._default_view) url = self.get_url('.' + self._default_view)
else: else:
url = url_for('.' + return_view) url = self.get_url('.' + return_view)
return redirect(url) return redirect(url)
...@@ -2,7 +2,7 @@ import os.path as op ...@@ -2,7 +2,7 @@ import os.path as op
from functools import wraps from functools import wraps
from flask import Blueprint, render_template, abort, g from flask import Blueprint, render_template, abort, g, url_for
from flask.ext.admin import babel from flask.ext.admin import babel
from flask.ext.admin._compat import with_metaclass from flask.ext.admin._compat import with_metaclass
from flask.ext.admin import helpers as h from flask.ext.admin import helpers as h
...@@ -271,6 +271,9 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -271,6 +271,9 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
kwargs['_ngettext'] = babel.ngettext kwargs['_ngettext'] = babel.ngettext
kwargs['h'] = h kwargs['h'] = h
# Expose get_url helper
kwargs['get_url'] = self.get_url
# Contribute extra arguments # Contribute extra arguments
kwargs.update(self._template_args) kwargs.update(self._template_args)
...@@ -331,6 +334,18 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)): ...@@ -331,6 +334,18 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
""" """
return abort(403) return abort(403)
def get_url(self, endpoint, **kwargs):
"""
Generate URL for the endpoint. If you want to customize URL generation
logic (persist some query string argument, for example), this is
right place to do it.
:param endpoint:
Flask endpoint name
:param kwargs:
Arguments for `url_for`
"""
return url_for(endpoint, **kwargs)
@property @property
def _debug(self): def _debug(self):
......
...@@ -7,7 +7,7 @@ import shutil ...@@ -7,7 +7,7 @@ import shutil
from operator import itemgetter from operator import itemgetter
from werkzeug import secure_filename from werkzeug import secure_filename
from flask import flash, url_for, redirect, abort, request, send_file from flask import flash, redirect, abort, request, send_file
from wtforms import fields, validators from wtforms import fields, validators
...@@ -302,14 +302,14 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -302,14 +302,14 @@ class FileAdmin(BaseView, ActionsMixin):
Additional arguments Additional arguments
""" """
if not path: if not path:
return url_for(endpoint) return self.get_url(endpoint)
else: else:
if self._on_windows: if self._on_windows:
path = path.replace('\\', '/') path = path.replace('\\', '/')
kwargs['path'] = path kwargs['path'] = path
return url_for(endpoint, **kwargs) return self.get_url(endpoint, **kwargs)
def _get_file_url(self, path): def _get_file_url(self, path):
""" """
...@@ -322,7 +322,8 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -322,7 +322,8 @@ class FileAdmin(BaseView, ActionsMixin):
route = '.edit' route = '.edit'
else: else:
route = '.download' route = '.download'
return url_for(route, path=path)
return self.get_url(route, path=path)
def _normalize_path(self, path): def _normalize_path(self, path):
""" """
...@@ -531,7 +532,7 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -531,7 +532,7 @@ class FileAdmin(BaseView, ActionsMixin):
# backward compatibility with base_url # backward compatibility with base_url
base_url = self.get_base_url() base_url = self.get_base_url()
if base_url: if base_url:
base_url = urljoin(url_for('.index'), base_url) base_url = urljoin(self.get_url('.index'), base_url)
return redirect(urljoin(base_url, path)) return redirect(urljoin(base_url, path))
return send_file(directory) return send_file(directory)
...@@ -580,7 +581,7 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -580,7 +581,7 @@ class FileAdmin(BaseView, ActionsMixin):
path = request.form.get('path') path = request.form.get('path')
if not path: if not path:
return redirect(url_for('.index')) return redirect(self.get_url('.index'))
# Get path and verify if it is valid # Get path and verify if it is valid
base_path, full_path, path = self._normalize_path(path) base_path, full_path, path = self._normalize_path(path)
...@@ -624,7 +625,7 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -624,7 +625,7 @@ class FileAdmin(BaseView, ActionsMixin):
path = request.args.get('path') path = request.args.get('path')
if not path: if not path:
return redirect(url_for('.index')) return redirect(self.get_url('.index'))
base_path, full_path, path = self._normalize_path(path) base_path, full_path, path = self._normalize_path(path)
...@@ -669,13 +670,15 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -669,13 +670,15 @@ class FileAdmin(BaseView, ActionsMixin):
""" """
Edit view method Edit view method
""" """
path = request.args.getlist('path')
next_url = None next_url = None
path = request.args.getlist('path')
if not path: if not path:
return redirect(url_for('.index')) return redirect(self.get_url('.index'))
if len(path) > 1: if len(path) > 1:
next_url = url_for('.edit', path=path[1:]) next_url = self.get_url('.edit', path=path[1:])
path = path[0] path = path[0]
base_path, full_path, path = self._normalize_path(path) base_path, full_path, path = self._normalize_path(path)
...@@ -753,4 +756,4 @@ class FileAdmin(BaseView, ActionsMixin): ...@@ -753,4 +756,4 @@ class FileAdmin(BaseView, ActionsMixin):
@action('edit', lazy_gettext('Edit')) @action('edit', lazy_gettext('Edit'))
def action_edit(self, items): def action_edit(self, items):
return redirect(url_for('.edit', path=items)) return redirect(self.get_url('.edit', path=items))
from flask import url_for
from jinja2 import Markup, escape from jinja2 import Markup, escape
from mongoengine.base import BaseList from mongoengine.base import BaseList
...@@ -20,7 +19,7 @@ def grid_formatter(view, value): ...@@ -20,7 +19,7 @@ def grid_formatter(view, value):
'<i class="icon-file"></i>%(name)s' + '<i class="icon-file"></i>%(name)s' +
'</a> %(size)dk (%(content_type)s)') % '</a> %(size)dk (%(content_type)s)') %
{ {
'url': url_for('.api_file_view', **args), 'url': view.get_url('.api_file_view', **args),
'name': escape(value.name), 'name': escape(value.name),
'size': value.length // 1024, 'size': value.length // 1024,
'content_type': escape(value.content_type) 'content_type': escape(value.content_type)
...@@ -36,8 +35,8 @@ def grid_image_formatter(view, value): ...@@ -36,8 +35,8 @@ def grid_image_formatter(view, value):
'<a href="%(url)s" target="_blank"><img src="%(thumb)s"/></a>' + '<a href="%(url)s" target="_blank"><img src="%(thumb)s"/></a>' +
'</div>') % '</div>') %
{ {
'url': url_for('.api_file_view', **helpers.make_gridfs_args(value)), 'url': view.get_url('.api_file_view', **helpers.make_gridfs_args(value)),
'thumb': url_for('.api_file_view', **helpers.make_thumb_args(value)), 'thumb': view.get_url('.api_file_view', **helpers.make_thumb_args(value)),
}) })
......
from wtforms.widgets import HTMLString, html_params from wtforms.widgets import HTMLString, html_params
from jinja2 import escape from jinja2 import escape
from flask import url_for
from mongoengine.fields import GridFSProxy, ImageGridFsProxy from mongoengine.fields import GridFSProxy, ImageGridFsProxy
from flask.ext.admin.helpers import get_url
from . import helpers from . import helpers
...@@ -53,7 +53,7 @@ class MongoImageInput(object): ...@@ -53,7 +53,7 @@ class MongoImageInput(object):
if field.data and isinstance(field.data, ImageGridFsProxy): if field.data and isinstance(field.data, ImageGridFsProxy):
args = helpers.make_thumb_args(field.data) args = helpers.make_thumb_args(field.data)
placeholder = self.template % { placeholder = self.template % {
'thumb': url_for('.api_file_view', **args), 'thumb': get_url('.api_file_view', **args),
'marker': '_%s-delete' % field.name 'marker': '_%s-delete' % field.name
} }
......
import os import os
import os.path as op import os.path as op
from flask import url_for
from werkzeug import secure_filename from werkzeug import secure_filename
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
...@@ -15,6 +13,7 @@ except ImportError: ...@@ -15,6 +13,7 @@ except ImportError:
from wtforms.utils import unset_value from wtforms.utils import unset_value
from flask.ext.admin.babel import gettext from flask.ext.admin.babel import gettext
from flask.ext.admin.helpers import get_url
from flask.ext.admin._compat import string_types, urljoin from flask.ext.admin._compat import string_types, urljoin
...@@ -106,7 +105,7 @@ class ImageUploadInput(object): ...@@ -106,7 +105,7 @@ class ImageUploadInput(object):
if field.url_relative_path: if field.url_relative_path:
filename = urljoin(field.url_relative_path, filename) filename = urljoin(field.url_relative_path, filename)
return url_for(field.endpoint, filename=filename) return get_url(field.endpoint, filename=filename)
# Fields # Fields
...@@ -356,8 +355,8 @@ class ImageUploadField(FileUploadField): ...@@ -356,8 +355,8 @@ class ImageUploadField(FileUploadField):
def pre_validate(self, form): def pre_validate(self, form):
super(ImageUploadField, self).pre_validate(form) super(ImageUploadField, self).pre_validate(form)
if (self.data and if (self.data and
isinstance(self.data, FileStorage) and isinstance(self.data, FileStorage) and
self.data.filename): self.data.filename):
try: try:
self.image = Image.open(self.data) self.image = Image.open(self.data)
......
from re import sub from re import sub
from jinja2 import contextfunction from jinja2 import contextfunction
from flask import g, request from flask import g, request, url_for
from wtforms.validators import DataRequired, InputRequired from wtforms.validators import DataRequired, InputRequired
from flask.ext.admin._compat import urljoin, urlparse from flask.ext.admin._compat import urljoin, urlparse
...@@ -20,6 +20,25 @@ def get_current_view(): ...@@ -20,6 +20,25 @@ def get_current_view():
return getattr(g, '_admin_view', None) return getattr(g, '_admin_view', None)
def get_url(endpoint, **kwargs):
"""
Alternative to Flask `url_for`.
If there's current administrative view, will call its `get_url`. If there's none - will
use generic `url_for`.
:param endpoint:
Endpoint name
:param kwargs:
View arguments
"""
view = get_current_view()
if not view:
return url_for(endpoint, **kwargs)
return view.get_url(endpoint, **kwargs)
def is_required_form_field(field): def is_required_form_field(field):
""" """
Check if form field has `DataRequired` or `InputRequired` validators. Check if form field has `DataRequired` or `InputRequired` validators.
......
...@@ -98,7 +98,7 @@ class MenuView(BaseMenu): ...@@ -98,7 +98,7 @@ class MenuView(BaseMenu):
if self._cached_url: if self._cached_url:
return self._cached_url return self._cached_url
self._cached_url = url_for('%s.%s' % (self._view.endpoint, self._view._default_view)) self._cached_url = self._view.get_url('%s.%s' % (self._view.endpoint, self._view._default_view))
return self._cached_url return self._cached_url
def is_active(self, view): def is_active(self, view):
......
This diff is collapsed.
from flask import url_for, json from flask import json
from wtforms.widgets import HTMLString, html_params from wtforms.widgets import HTMLString, html_params
from flask.ext.admin._compat import as_unicode from flask.ext.admin._compat import as_unicode
from flask.ext.admin.babel import gettext from flask.ext.admin.babel import gettext
from flask.ext.admin.helpers import get_url
from flask.ext.admin.form import RenderTemplateWidget from flask.ext.admin.form import RenderTemplateWidget
...@@ -26,7 +27,7 @@ class AjaxSelect2Widget(object): ...@@ -26,7 +27,7 @@ class AjaxSelect2Widget(object):
def __call__(self, field, **kwargs): def __call__(self, field, **kwargs):
kwargs['data-role'] = u'select2-ajax' kwargs['data-role'] = u'select2-ajax'
kwargs['data-url'] = url_for('.ajax_lookup', name=field.loader.name) kwargs['data-url'] = get_url('.ajax_lookup', name=field.loader.name)
allow_blank = getattr(field, 'allow_blank', False) allow_blank = getattr(field, 'allow_blank', False)
if allow_blank and not self.multiple: if allow_blank and not self.multiple:
......
...@@ -50,14 +50,14 @@ ...@@ -50,14 +50,14 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{% if admin_view.can_rename and path and name != '..' %} {% if admin_view.can_rename and path and name != '..' %}
<a class="icon" href="{{ url_for('.rename', path=path) }}"> <a class="icon" href="{{ get_url('.rename', path=path) }}">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</a> </a>
{% endif %} {% endif %}
{%- if admin_view.can_delete and path -%} {%- if admin_view.can_delete and path -%}
{% if is_dir %} {% if is_dir %}
{% if name != '..' and admin_view.can_delete_dirs %} {% if name != '..' and admin_view.can_delete_dirs %}
<form class="icon" method="POST" action="{{ url_for('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> <input type="hidden" name="path" value="{{ path }}"></input>
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
<i class="icon-remove"></i> <i class="icon-remove"></i>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</form> </form>
{% endif %} {% endif %}
{% else %} {% else %}
<form class="icon" method="POST" action="{{ url_for('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> <input type="hidden" name="path" value="{{ path }}"></input>
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
<i class="icon-remove"></i> <i class="icon-remove"></i>
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
</div> </div>
{% endblock %} {% endblock %}
{% block actions %} {% block actions %}
{{ actionslib.form(actions, url_for('.action_view')) }} {{ actionslib.form(actions, get_url('.action_view')) }}
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</li> </li>
{% if admin_view.can_create %} {% if admin_view.can_create %}
<li> <li>
<a href="{{ url_for('.create_view', url=return_url) }}" title="{{ _gettext('Create new record') }}">{{ _gettext('Create') }}</a> <a href="{{ get_url('.create_view', url=return_url) }}" title="{{ _gettext('Create new record') }}">{{ _gettext('Create') }}</a>
</li> </li>
{% endif %} {% endif %}
...@@ -102,12 +102,12 @@ ...@@ -102,12 +102,12 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{%- if admin_view.can_edit -%} {%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}"> <a class="icon" href="{{ get_url('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<i class="icon-pencil"></i> <i class="icon-pencil"></i>
</a> </a>
{%- endif -%} {%- endif -%}
{%- if admin_view.can_delete -%} {%- if admin_view.can_delete -%}
<form class="icon" method="POST" action="{{ url_for('.delete_view', id=get_pk_value(row), url=return_url) }}"> <form class="icon" method="POST" action="{{ get_url('.delete_view', id=get_pk_value(row), url=return_url) }}">
{% if csrf_token %} {% if csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %} {% endif %}
...@@ -139,7 +139,7 @@ ...@@ -139,7 +139,7 @@
{{ lib.pager(page, num_pages, pager_url) }} {{ lib.pager(page, num_pages, pager_url) }}
{% endblock %} {% endblock %}
{{ actionlib.form(actions, url_for('.action_view')) }} {{ actionlib.form(actions, get_url('.action_view')) }}
{% endblock %} {% endblock %}
{% block tail %} {% block tail %}
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script> <script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script>
<script language="javascript"> <script language="javascript">
$(function() { $(function() {
var redisCli = new RedisCli({{ url_for('.execute_view')|tojson }}); var redisCli = new RedisCli({{ get_url('.execute_view')|tojson }});
}); });
</script> </script>
{% endblock %} {% endblock %}
...@@ -50,14 +50,14 @@ ...@@ -50,14 +50,14 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{% if admin_view.can_rename and path and name != '..' %} {% if admin_view.can_rename and path and name != '..' %}
<a class="icon" href="{{ url_for('.rename', path=path) }}"> <a class="icon" href="{{ get_url('.rename', path=path) }}">
<i class="glyphicon glyphicon-pencil"></i> <i class="glyphicon glyphicon-pencil"></i>
</a> </a>
{% endif %} {% endif %}
{%- if admin_view.can_delete and path -%} {%- if admin_view.can_delete and path -%}
{% if is_dir %} {% if is_dir %}
{% if name != '..' and admin_view.can_delete_dirs %} {% if name != '..' and admin_view.can_delete_dirs %}
<form class="icon" method="POST" action="{{ url_for('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> <input type="hidden" name="path" value="{{ path }}"></input>
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\' recursively?', name=name) }}')">
<i class="icon-remove"></i> <i class="icon-remove"></i>
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</form> </form>
{% endif %} {% endif %}
{% else %} {% else %}
<form class="icon" method="POST" action="{{ url_for('.delete') }}"> <form class="icon" method="POST" action="{{ get_url('.delete') }}">
<input type="hidden" name="path" value="{{ path }}"></input> <input type="hidden" name="path" value="{{ path }}"></input>
<button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')"> <button onclick="return confirm('{{ _gettext('Are you sure you want to delete \\\'%(name)s\\\'?', name=name) }}')">
<i class="glyphicon glyphicon-trash"></i> <i class="glyphicon glyphicon-trash"></i>
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
</div> </div>
{% endblock %} {% endblock %}
{% block actions %} {% block actions %}
{{ actionslib.form(actions, url_for('.action_view')) }} {{ actionslib.form(actions, get_url('.action_view')) }}
{% endblock %} {% endblock %}
{% endblock %} {% endblock %}
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</li> </li>
{% if admin_view.can_create %} {% if admin_view.can_create %}
<li> <li>
<a href="{{ url_for('.create_view', url=return_url) }}" title="{{ _gettext('Create new record') }}">{{ _gettext('Create') }}</a> <a href="{{ get_url('.create_view', url=return_url) }}" title="{{ _gettext('Create new record') }}">{{ _gettext('Create') }}</a>
</li> </li>
{% endif %} {% endif %}
...@@ -102,12 +102,12 @@ ...@@ -102,12 +102,12 @@
<td> <td>
{% block list_row_actions scoped %} {% block list_row_actions scoped %}
{%- if admin_view.can_edit -%} {%- if admin_view.can_edit -%}
<a class="icon" href="{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}"> <a class="icon" href="{{ get_url('.edit_view', id=get_pk_value(row), url=return_url) }}" title="{{ _gettext('Edit record') }}">
<span class="glyphicon glyphicon-pencil"></span> <span class="glyphicon glyphicon-pencil"></span>
</a> </a>
{%- endif -%} {%- endif -%}
{%- if admin_view.can_delete -%} {%- if admin_view.can_delete -%}
<form class="icon" method="POST" action="{{ url_for('.delete_view', id=get_pk_value(row), url=return_url) }}"> <form class="icon" method="POST" action="{{ get_url('.delete_view', id=get_pk_value(row), url=return_url) }}">
{% if csrf_token %} {% if csrf_token %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% endif %} {% endif %}
...@@ -139,7 +139,7 @@ ...@@ -139,7 +139,7 @@
{{ lib.pager(page, num_pages, pager_url) }} {{ lib.pager(page, num_pages, pager_url) }}
{% endblock %} {% endblock %}
{{ actionlib.form(actions, url_for('.action_view')) }} {{ actionlib.form(actions, get_url('.action_view')) }}
{% endblock %} {% endblock %}
{% block tail %} {% block tail %}
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script> <script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script>
<script language="javascript"> <script language="javascript">
$(function() { $(function() {
var redisCli = new RedisCli({{ url_for('.execute_view')|tojson }}); var redisCli = new RedisCli({{ admin_view.get_url('.execute_view')|tojson }});
}); });
</script> </script>
{% endblock %} {% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment