Repository: flask-admin/flask-admin

Issue:
Why FileAdmin.base_url is required?
Docs are a bit confuse about this `base_url` parameter.

If I want to serve my files from upload path I have to add this view:

```
# untested code
f = FileAdmin('path/to/upload', 'upload/')

@app.route('fileadmin/upload/<path:path>')
def download(path):
    return send_from_directory(f.base_path, path)
```

Of course I can subclass FileAdmin to add this route in a better way, but I expect it built-in.



Discussion:

Resolving patch:
diff --git a/flask_admin/contrib/fileadmin.py b/flask_admin/contrib/fileadmin.py
--- a/flask_admin/contrib/fileadmin.py
+++ b/flask_admin/contrib/fileadmin.py
@@ -7,7 +7,7 @@
 from operator import itemgetter
 from werkzeug import secure_filename
 
-from flask import flash, url_for, redirect, abort, request
+from flask import flash, url_for, redirect, abort, request, send_from_directory
 
 from wtforms import fields, validators
 
@@ -64,13 +64,12 @@ class FileAdmin(BaseView, ActionsMixin):
     """
         Simple file-management interface.
 
-        Requires two parameters:
-
         :param path:
             Path to the directory which will be managed
-        :param url:
-            Base URL for the directory. Will be used to generate
-            static links to the files.
+        :param base_url:
+            Optional base URL for the directory. Will be used to generate
+            static links to the files. If not defined, a route will be created
+            to serve uploaded files.
 
         Sample usage::
 
@@ -86,6 +85,11 @@ class FileAdmin(BaseView, ActionsMixin):
         Is file upload allowed.
     """
 
+    can_download = True
+    """
+        Is file download allowed.
+    """
+
     can_delete = True
     """
         Is file deletion allowed.
@@ -151,7 +155,7 @@ class MyAdmin(FileAdmin):
         Edit template
     """
 
-    def __init__(self, base_path, base_url,
+    def __init__(self, base_path, base_url=None,
                  name=None, category=None, endpoint=None, url=None,
                  verify_path=True):
         """
@@ -310,10 +314,10 @@ def _get_file_url(self, path):
                 Static file path
         """
         if self.is_file_editable(path):
-            return url_for(".edit", path=path)
+            route = '.edit'
         else:
-            base_url = self.get_base_url()
-            return urljoin(base_url, path)
+            route = '.download'
+        return url_for(route, path=path)
 
     def _normalize_path(self, path):
         """
@@ -503,6 +507,27 @@ def upload(self, path=None):
 
         return self.render(self.upload_template, form=form)
 
+    @expose('/download/<path:path>')
+    def download(self, path=None):
+        """
+            Download view method.
+
+            :param path:
+                File path.
+        """
+        if not self.can_download:
+            abort(404)
+
+        base_path, directory, path = self._normalize_path(path)
+
+        # backward compatibility with base_url
+        base_url = self.get_base_url()
+        if base_url:
+            base_url = urljoin(url_for('.index'), base_url)
+            return redirect(urljoin(base_url, path))
+
+        return send_from_directory(base_path, path)
+
     @expose('/mkdir/', methods=('GET', 'POST'))
     @expose('/mkdir/<path:path>', methods=('GET', 'POST'))
     def mkdir(self, path=None):


Test patch: