Hello World,
I have a table named File is supposed to be referenced by other tables in
an attempt to simulate the "upload' field type. This table is coupled with
a Folder table and simulates a file system in the database.
File Fields:
Field('name', 'string', label=T("Name"), requires=IS_NOT_EMPTY(), required=
True, notnull=True, length=255),
Field('caption', 'text', label=T("Caption")),
Field('content_type', 'string', label=T("Content Type"), requires=
IS_NOT_EMPTY(), required=True, notnull=True, length=255),
Field('data_storage', 'upload', label=T("Storage"), uploadfolder=cls.
__UPLOAD_FOLDER__, uploadseparate=True, autodelete=True, required=True,
notnull=True),
Field('folder_id', cls.__PARENT_CLASS__.Reference(), label=T("Folder"),
ondelete='CASCADE'),
Field('order_hint', 'integer', label=T("Order")),
Field('absolute_path', compute=lambda row: absolute_path_from_storage(row.
data_storage, cls.__UPLOAD_FOLDER__))
I have built an ORM around pydal, so the code looks a little funny - but
this problem relates directly to web2py.
Now, I have another table that references this file table. This is fine and
works well with Smart Grid as long as you are only using file(s) that are
already in the database. Smart grid provides the usual popup button on an
html page and you can choose which file you want to reference.
Here is an example field set from a table Building that references the File
Table:
Field('address', 'string', label=T("Address"), requires=IS_NOT_EMPTY(),
required=True, notnull=True, length=255),
Field('description', 'text', label=T("Description")),
Field('picture_id', File.Reference(), label=T("Picture"), ondelete='SET
NULL'),
Field('electric_company', ElectricCompany.Reference(), label=T("Electric
Company"), ondelete='SET NULL'),
Field('school_district', SchoolDistrict.Reference(), label=T("School
District"), ondelete='SET NULL'),
Field('services_ids', Service.ListReference(), label=T("Included Services"),
ondelete='SET NULL')
File.Reference() == 'reference plugin_substratum_file' (The pydal table
name for the File table is plugin_substratum_file)
Obviously the file reference is now like any other table reference - there
is no upload capability. I have written a widget and a validator (that are
automatically added to the picture_id field by my ORM) that create a file
input for the field through SQLFORM:
class FileUploadWidget(FormWidget):
_class = 'file'
DEFAULT_WIDTH = '150px'
ID_DELETE_SUFFIX = '__delete'
GENERIC_DESCRIPTION = 'file ## download'
DELETE_FILE = 'delete'
@classmethod
def widget(cls, field, value, **attributes):
from gluon.contrib.simplejson import JSONEncoder
"""
generates a INPUT file tag.
Optionally provides an A link to the file, including a checkbox so
the file can be deleted.
All is wrapped in a DIV.
see also: `FormWidget.widget`
Args:
field: the field
value: the field value
download_url: url for the file download (default = None)
"""
default = dict(_type='file')
attr = cls._attributes(field, default, **attributes)
attr['_data-show-upload'] = 'false'
attr['_data-show-close'] = 'false'
attr['_data-overwrite-initial'] = 'true'
file = File.With_ID(int(value)) if value else None
if file:
json = JSONEncoder()
attr['_data-initial-preview'] = "<img src={!r} />".format(file.
url)
attr['_data-initial-caption'] = file.name
attr['_data-initial-preview-config'] = json.encode([{
'caption' : file.name,
'key' : file.id
}])
attr['_data-layout-templates'] = json.encode({
'actionDelete' : ""
})
return INPUT(**attr)
@classmethod
def represent(cls, field, value):
"""
How to represent the file:
- with download url and if it is an image: <A href=...><IMG ...></A>
- otherwise with download url: <A href=...>file</A>
- otherwise: file
Args:
field: the field
value: the field value
download_url: url for the file download (default = None)
"""
inp = current.T(cls.GENERIC_DESCRIPTION)
file = File.With_ID(int(value)) if value else None
if file:
url = file.url
if cls.is_image(file):
inp = IMG(_src=url, _width=cls.DEFAULT_WIDTH)
inp = A(inp, _href=url)
return inp
@staticmethod
def is_image(file):
"""
Tries to check if the filename provided references to an image
Checking is based on mime type.
Args:
file: File record
"""
mime_type = file.content_type
if len(mime_type) > 5 and mime_type[:5] == 'image':
return True
return False
The problem is that now the returned value from the HTML form is a
FieldStorage object instead of the id of a row from the File table. What I
would like to accomplish is the creation of a new File row, then changing
the picture_id field value into a reference to that row - this should
happen before creation or update - but it should not happen if the form
doesn't fully validate.
My custom validator creates a FileUploadToken object that differentiates it
from normal 'upload' field types so that I can catch it later in the
onvalidation function that I pass to SQLFORM.smartgrid:
class FileUploadToken(object):
def __init__(self, field_storage):
self.field_storage = field_storage
class FILE_VALIDATOR(Validator):
def __call__(self, value):
from cgi import FieldStorage
error = None
if isinstance(value, basestring):
if not value:
value = None
elif isinstance(value, FieldStorage):
value = FileUploadToken(value)
return (value, error)
Then in the onvalidation function that I pass to SQLFORM.smartgrid I find
any FileUploadToken objects and create the new File record and reassign the
field value to the id of the new record:
def onvalidation(form, super_call=super_validation):
from plugin_substratum import File
for key,value in form.vars.iteritems():
if isinstance(value, FileUploadToken):
new_file = File.Upload(value.field_storage)
form.vars[key] = new_file.id
This all feels extremely contrived and I have to believe that there is a
more direct way to accomplish this end. This solution also depends on the
onvalidation function being passed to SQLFORM.smartgrid - which is
unacceptable.
Is there a better way to do this? Would a custom field type be a more
elegant solution?
--
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.