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.