Upload field with clear text filename

116 views
Skip to first unread message

Joe Barnhart

unread,
Dec 28, 2012, 4:15:52 AM12/28/12
to web...@googlegroups.com
I'm not sure why this is difficult, but I see many posts about this when I search, yet none exactly work for me.  This seems like it should be easy but is surprisingly difficult in web2py.

I want to keep a table of uploaded files with the uploads in a "blob" field and the names in clear text in a "filename" field.  Like this model:

db.define_table("fileobject",
   
Field("filename","string",length=50,readable=False,writable=False),
   
Field("upload","upload",uploadfield="object_data"),
   
Field("object_type","string",length=20,readable=False,writable=False),
   
Field("object_data","blob"),
   
Field("owner","reference auth_user",default=auth.user_id,readable=False,writable=False),
   
Field("saved","datetime",default=datetime.now(),readable=False,writable=False),
   
Field("state","string",length=16,readable=False,writable=False),
    migrate
=settings.migrate)

Reading the relevant posts and the online book lead me to believe the way is with a controller like this one:

def index():
    form
=SQLFORM(db.fileobject)
   
if request.vars.upload:
        form
.vars.filename = request.vars.upload.filename
   
if form.process().accepted:
        response
.flash = 'form accepted'
   
elif form.errors:
        response
.flash = 'form has errors'
   
return dict(form=form)

And yet this controller does not work.  The blob is filled in but the filename is ignored and shows in the table as "None".  In fact, form.vars is an empty collection and placing form.vars.filename in it does not produce an error, but it is ignored by the form processing.  In addition to the filename, I'd like to add the information about the object type etc. at the same point in the process, i.e. after the file has been chosen but before it has been loaded into the database.

I've been trying to scan through the code (I'm using 2.3.2 source) to answer my own questions about the upload field but it's just taking too much time.  The upload field is one of those parts of web2py that you either love or... uh... don't.  To me it seems designed for a very specific use model.  If my use model deviates too much then using "upload" is like pounding square pegs into round holes.  I'm tired of pounding.  Someone show me how to whittle off the corners of my square peg!

(You would think this would be an easy task, yet from the number of posts on this exact topic I know I'm in the company of a large number of others.)


Anthony

unread,
Dec 28, 2012, 5:18:24 AM12/28/12
to web...@googlegroups.com
Are you saying you want the name in the "filename" field to be encoded into the string stored in the "upload" field so the file has the name in the "filename" field upon download? If so, then try:

if request.vars.upload and request.vars.filename:
    request
.vars.upload.filename = request.vars.filename
form
= SQLFORM(db.fileobject)

That will re-assign the filename of the uploaded file to the value entered by the user, and web2py will then encode that user-entered filename into the new filename. Another option is to do nothing upon upload and instead replace the original filename with the user-entered filename upon download (which you could do via a custom_retrieve function for the "upload" field).

Note, web2py relies on the filename extension to set the HTTP headers appropriately upon download, so you may want to add some logic to obtain the original filename extension in case the user fails to enter it.

Anthony

Joe Barnhart

unread,
Dec 28, 2012, 3:23:10 PM12/28/12
to
Hi Anthony --

I want the opposite of this -- I want the original text filename, not a replacement.  The book example I followed did not work.

I found at least one problem -- the test "if request.vars.upload:" does not work.  Even though the upload field has a valid filename, the FieldStorage object returns "False" for the test.  In the book the recommended test was "if request.vars.upload != None" but that was found to have a walkback if the submit button was pressed without choosing a filename, as request.vars.upload returns an empty string in that case.

-- Joe

Anthony

unread,
Dec 28, 2012, 6:48:26 PM12/28/12
to web...@googlegroups.com
I see. I think the problem is that you have set the writable attribute of the filename field to False. In that case, try setting the default value of that field:

if request.vars.upload:
    db
.fileobject.filename.default = request.vars.upload.filename
form
= SQLFORM(db.fileobject)

Anthony

On Friday, December 28, 2012 2:28:53 PM UTC-5, Joe Barnhart wrote:
Hi Anthony --

I want the opposite of this -- I want the original text filename, not a replacement.  The book example I followed did not work.

I found at least one problem -- the test "if request.vars.upload:" does not work.  Even though the upload field has a valid filename, the FieldStorage object returns "False" for the test.  In the book the recommended test was "if request.vars.upload != None" but that was found to have a walkback if the submit button was pressed without choosing a filename, as request.vars.upload returns an empty string in that case.

-- Joe

On Friday, December 28, 2012 2:18:24 AM UTC-8, Anthony wrote:

Joe Barnhart

unread,
Dec 29, 2012, 8:04:05 PM12/29/12
to web...@googlegroups.com
I was trying to keep the filename out of the generated form since I want it set "automatically" in the process of uploading the document.  Is there a better way to accomplish that?

BTW, the test "if request.vars.upload" does not work.  It returns "False" even if the upload field has been set.  I had to use the much uglier:

if request.vars.upload!=None and request.vars.upload!='':

-- Joe

Anthony

unread,
Dec 29, 2012, 8:42:33 PM12/29/12
to web...@googlegroups.com
Setting writable to False is probably the best way to keep that field out of the form -- that's why I suggested setting the default value of the field instead. Does that not work?

Anthony

Alan Etkin

unread,
Dec 30, 2012, 3:51:11 PM12/30/12
to web...@googlegroups.com
El viernes, 28 de diciembre de 2012 06:15:52 UTC-3, Joe Barnhart escribió:
I'm not sure why this is difficult, but I see many posts about this when I search, yet none exactly work for me.  This seems like it should be easy but is surprisingly difficult in web2py.

A possible workaround (worked with SQLite and web2py trunk)

def index():
    form
=SQLFORM(db.fileobject)
   
if form.process().accepted:
        form
.vars.filename = request.vars.theupload.filename
        db
.fileobject[form.vars.id].update_record(filename=form.vars.filename)

   
elif form.errors:
        response
.flash = 'form has errors'
   
return dict(form=form)

The caveat is that you need an extra db command per request

Still, the fact that form.vars.filename doesn't write the new value on db insert seems to be a bug.

Massimo Di Pierro

unread,
Dec 30, 2012, 4:13:50 PM12/30/12
to web...@googlegroups.com
This

    form=SQLFORM(db.fileobject)
    
if request.vars.upload:
        form
.vars.filename = request.vars.upload.
filename
    
if form.process().accepted: ...

should work. Please open a ticket.

Alan Etkin

unread,
Dec 30, 2012, 4:33:54 PM12/30/12
to web...@googlegroups.com
My bad:

Assigning to a form.vars item before calling to .process() works

The issue is that the first code posted by Joe tests for the boolean value of form.vars.upload (an instance of the file storage object) and that object always evaluates to False, so:

if request.vars.update:
    <assign to form.var item>

is omitted.

Reply all
Reply to author
Forward
0 new messages