And in my case, using the represent option to feed an id variable to the 
URL,  a download function like this works:


from gluon.contenttype import contenttype

def download_dataset():
    
    # get the id and record
    ds_id = request.vars['id']

    if ds_id is None:
        # non-existent id provided
        session.flash = "Database id not provided"
        redirect(URL('datasets','administer_datasets'))
    
    try:
        ds_id = int(ds_id)
    except ValueError:
        # non-integer id provided
        session.flash = "Database id not an integer"
        redirect(URL('datasets','administer_datasets'))
    
    record = db.datasets[ds_id]
    
    if record is None:
        # non-existent id provided
        session.flash = "Database record id does not exist"
        redirect(URL('datasets','administer_datasets'))
    
    # get the file name into the download header
    file_header = "attachment; filename=" + record.file_name
    
    # set up the response
    response.headers['ContentType'] = contenttype(record.file_name)
    response.headers['Content-Disposition'] = file_header
    
    # get a stream of the file
    path = os.path.join(request.folder, 'uploads', 'datasets', 
str(record.dataset_id), record.file)
    stream = open(path, 'rb')
    return response.stream(stream)



On Saturday, 3 February 2018 21:44:23 UTC, David Orme wrote:
>
> Thanks, Anthony. So to sum up what I think the options are:
>
> 1) The code laying out the standard SQLFORM.grid record view handles the 
> display of each field and - for upload fields - the download link uses: 
>
> https://.../controller/sqlform_grid_function/download/file_name
>
> That can be changed using the upload argument to SQLFORM grid. If you pass 
> in a string (like the output of URL) then file name value is appended,  so:
>
> form = SQLFORM.grid(..., upload = URL('dataset', 'download_dataset'), ...)
>
> will set the download link to:
>
> https://.../dataset/download_dataset/file_name
>
> You can pass in a function instead, in which case the output of the 
> function is used as the link (*without* appending the file name value). 
> So if you do:
>
> form = SQLFORM.grid(..., upload = lambda value: URL('datasets', 
> 'download_dataset'), ...)
>
> then the link is  not terribly functional:
>
> https://.../dataset/download_dataset
>
> The only local variable that function has access to is the file name, so 
> this bit of code provides a function that duplicates just providing the URL 
> as a string:
>
> form = SQLFORM.grid(..., upload = lambda value: URL('datasets', 
> 'download_dataset', value), ...)
>
> That isn't particularly useful, but the value could be used in a more 
> complex way. For example, as Anthony says, by looking up the filename 
> (which will be unique, thanks to the random component of the internal file 
> names) in the database table and using this to populate a link with the 
> custom download information. For example, this function allows the URL to 
> be expanded to use other parts of the record for that file:
>
> def _lookup(value):
>     record = db(db.datasets.file == value).select().first()
>     return URL('datasets','download_dataset', args=[record.dataset_id, 
> record.file])
>     
> form = SQLFORM.grid(...,  upload = lambda value: _lookup(value), ...)
>
> That produces links like this (for a record with dataset_id = 15):
>
> https://.../datasets/download_dataset/15/file_name
>
> I don't think there is any way to get the default download controller to 
> know about the nested download - it uses the field.retrieve method to get 
> the data and that doesn't know about the record specific upload folder. So, 
> I think you *have to* then use a custom function to provide the download. 
>
> Another alternative is to change the representation of the file field in 
> the controller function before creating the SQLFORM.grid. For example:
>
> db.datasets.file.represent = lambda value, row: A('Download file', _href=
> URL('datasets', 'download_dataset', vars={'id': row.id}))
>
> I thought that would derail the upload widget in the SQLFORM edit view but 
> it doesn't seem to. Its probably a simpler way to get a custom download 
> link that points to the record rather than just the filename, but doesn't 
> get around the need for a custom download function.
>
>
> On Saturday, 3 February 2018 17:37:32 UTC, Anthony wrote:
>>
>> On Saturday, February 3, 2018 at 1:58:26 AM UTC-5, David Orme wrote:
>>>
>>> So at the moment, what users are seeing is a controller presenting 
>>> SQLFORM.grid of dataset records, and then in the 'view' argument to that 
>>> controller for a particular record, the file field is shown as the output 
>>> of the represent method of UploadWidget (so the word 'file' wrapped up 
>>> with the download link, which is ignorant of the subfolder). It isn't 
>>> obvious to me that there is a way to insert new_ds_id into that mechanism?
>>>
>>
>> The "upload" argument of SQLFORM.grid can be a function that takes the 
>> value and returns a URL, but you are still stuck having to figure out the 
>> URL from the filename, which will require a database lookup per row of the 
>> grid. An alternative is to not display the default file column in the grid 
>> and instead use the "links" argument to generate a custom column with 
>> custom download links (see "links" under 
>> http://web2py.com/books/default/chapter/29/07/forms-and-validators#SQLFORM-grid-signature).
>>  
>> Another option is to create a custom column using a virtual field.
>>
>> Anthony
>>
>

-- 
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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to