Assuming you are talking about the standard download() function in the 
default.py controller of the scaffolding application, note that it uses 
response.download() to retrieve the file, and as per the documentation, it 
looks at only the *last* URL arg to obtain the filename (from which it 
extracts the database table name and field name in order to get the 
uploadfolder and retrieve the file). So, in your link URL:

URL('download', args=(str(client_id) + '/attachments/' + r['attached_file'])

The str(client_id) and "attachements" parts of the URL are completely 
ignored and therefore unnecessary.

Of course, because response.download() works by getting the uploadfolder 
from the upload field definition, it requires that the relevant database 
model actually be defined. In your case, though, you are only defining the 
db.attachment_module table when inside the module_based() function, so when 
in the download() function, that model doesn't exist, and 
response.download() therefore cannot find the relevant upload folder.

If you don't want to have to ensure that the model has been defined when 
the download() function is called, you can continue using links like the 
ones you have generated, but in that case you won't be able to use 
response.download() and should instead use response.stream(). The former is 
designed to work specifically with upload fields in DAL models (and 
therefore requires access to the models), whereas the latter is a more 
general method for streaming any file back to the browser (though it 
requires that you know the full path to the file on the filesystem).

Anthony

On Monday, May 9, 2016 at 10:29:00 AM UTC-4, Andy W wrote:
>
> I have a multi-tenant application, where users can upload files. I save 
> these in a different directory for each client (tenant), so I can keep tabs 
> on the overall disk space and number of files uploaded by each.
>
> This works fine when the table is defined in a model file - from the view 
> I can download existing files and upload new ones:
>
> Model:
> from gluon import *
> import os
> client_id=3 #hard coded here for illustration
>            
> db.define_table('attachment_model',
>     Field('attached_file', 'upload', label="Upload new file", 
>           uploadfolder=os.path.join(request.folder, 'uploads', str(
> client_id), 'attachments'),
>           requires=IS_NOT_EMPTY(), autodelete=True),
>     Field('filename', type='string', length=150, writable=False),
>     migrate=True)
>
> Controller:
> def model_based():
>     db.attachment_model.filename.readable=False
>     form_attachment=SQLFORM(db.attachment_model,
>                  autodelete=True, labels=None, deletable=True,
>                  fields=['attached_file'], submit_button='Attach file')
>     if hasattr(request.vars.attached_file, "filename"):
>         #save original file name
>         form_attachment.vars.filename=request.vars.attached_file.filename
>     if form_attachment.process().accepted:
>         response.flash = 'attachment saved'
>     elif form_attachment.errors:
>         response.flash = 'form has errors'
>     # list existing attachments
>     rows=db(db.attachment_model.id>0).select()
>     response.view = 'attachment.html'
>     return dict(form_attachment=form_attachment,
>                 rows=rows)
>
> View:
> {{extend 'layout.html'}}
> <h1>
>     Attached files
> </h1>
>     <table class="table-striped">
>         <thead>
>             <tr>
>                 <td>id</td>
>                 <td>filename</td>
>             </tr>
>         </thead>
>         <tbody>
>             {{for r in rows:}}
>                 <tr>
>                     <td>{{=r.id}}</td>
>                     <td>{{=A(r.filename, _href=URL('download', args=(str(
> client_id) + '/attachments/' + r['attached_file'])))}}</td>
>                 </tr>
>             {{pass}}
>         </tbody>
>     </table>
>     
> <h2>Add new attachment</h2>
> <div class="form-inline well">
>     {{=form_attachment}}
> </div>
>
> My issue is when I re-write the above so the model definition is moved to 
> a module.
>
> Module mod_attachment.py:
> from gluon import *
> import os
>
> class Attachment_module(object):
>     def __init__(self, db):
>         self.db = db
>
>     def define_tables(self):
>         db = self.db
>         client_id=3
>         if not 'attachment_module' in db.tables:
>             db.define_table('attachment_module',
>                 Field('attached_file', 'upload', label="Upload new file", 
>                       uploadfolder=os.path.join(current.request.folder, 
> 'uploads', str(client_id), 'attachments'), 
>                       requires=IS_NOT_EMPTY(), autodelete=True),
>                 Field('filename', type='string', length=150, writable=
> False),
>                 Field('request_tenant', type='integer', default=client_id,
>                       readable=False, writable=False),
>                 migrate=True)
>
> Revised controller:
> def module_based():
>     from mod_attachment import Attachment_module
>     attachment_module = Attachment_module(db) 
>     attachment_module.define_tables()
>     db.attachment_module.filename.readable=False
>     form_attachment=SQLFORM(db.attachment_module,
>                  autodelete=True, labels=None, deletable=True,
>                  fields=['attached_file'], submit_button='Attach file')
>     if hasattr(request.vars.attached_file, "filename"):
>         #save original file name
>         form_attachment.vars.filename=request.vars.attached_file.filename
>     if form_attachment.process().accepted:
>         response.flash = 'attachment saved'
>     elif form_attachment.errors:
>         response.flash = 'form has errors'
>     # list existing attachments
>     rows=db(db.attachment_module.id>0).select()
>     response.view = 'attachment.html'
>     return dict(form_attachment=form_attachment,
>                 rows=rows)
>
> With this approach, I can still list the uploaded files and add additional 
> ones (using the same view as before), but the download function no longer 
> works.
>
> So my question is why not? Do I have to modify the standard download 
> function, and how?
> Hope these are not daft questions - any pointers would be appreciated.
>
> Andy
>
>
>
>
>
>
>

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to