Usage

Integration of records and files for Invenio.

Invenio-Records-Files provides basic API for integrating Invenio-Records and Invenio-Files-REST.

Initialization

First create a Flask application:

>>> from flask import Flask
>>> app = Flask('myapp')
>>> app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'

Records-Files have no Flask extension, however it is dependent on Invenio-Records and Invenio-Files-REST which must be initialized first:

>>> from invenio_db import InvenioDB
>>> ext_db = InvenioDB(app)
>>> from invenio_records import InvenioRecords
>>> from invenio_files_rest import InvenioFilesREST
>>> ext_filesrest = InvenioFilesREST(app)
>>> ext_records = InvenioRecords(app)

In order for the following examples to work, you need to work within an Flask application context so let’s push one:

>>> ctx = app.app_context()
>>> ctx.push()

Also, for the examples to work we need to create the database and tables (note, in this example we use an in-memory SQLite database):

>>> from invenio_db import db
>>> db.create_all()

Last, since we’re managing files, we need to create a base location. Here we will create a location in a temporary directory:

>>> import tempfile
>>> tmppath = tempfile.mkdtemp()
>>> from invenio_files_rest.models import Location
>>> db.session.add(Location(name='default', uri=tmppath, default=True))
>>> db.session.commit()

Creating a record

You use Invenio-Records-Files basic API by importing invenio_records_files.api.Record instead of invenio_records.api.Record:

>>> from invenio_records_files.api import Record

This records class has special property files through which you can access and create files. By default this property is None:

>>> record = Record.create({})
>>> record.files is None
True

This is because no bucket have been assigned to the record yet.

Assigning a bucket

You assign a bucket to a record through invenio_records_files.models.RecordsBuckets:

>>> from invenio_files_rest.models import Bucket
>>> from invenio_records_files.models import RecordsBuckets
>>> bucket = Bucket.create()
>>> record_buckets = RecordsBuckets.create(record=record.model, bucket=bucket)

Normally the bucket creation and bucket to record assignment is done by an external module (e.g. Invenio-Deposit is one example of this).

The files property now has a value and we can e.g. ask for the number of files:

>>> len(record.files)
0

Creating files

We are now ready to create our first file using the Invenio-Records-Files API:

>>> from six import BytesIO
>>> record.files['hello.txt'] = BytesIO(b'Hello, World')

In above example we create a file named hello.txt and assigns a stream like object which will be saved as a new object in the bucket.

Accessing files

We can access the just stored file through the same API:

>>> len(record.files)
1
>>> 'hello.txt' in record.files
True
>>> fileobj = record.files['hello.txt']
>>> print(fileobj.key)
hello.txt

Metadata for files

Besides creating files we can also assign metadata to files:

>>> fileobj['filetype'] = 'txt'
>>> print(record.files['hello.txt']['filetype'])
txt

Certain key names are however reserved cannot be used for setting metadata:

>>> fileobj['key'] = 'test'
Traceback (most recent call last):
  ...
KeyError: 'key'

The reserved key names are all the properties which exists on invenio_files_rest.models:ObjectVersion.

You can however still use the reserved keys for getting metadata:

>>> print(fileobj['key'])
hello.txt

Dumping files

You can make a dictionary of all files

>>> dump = record.files.dumps()
>>> for k in sorted(dump[0].keys()):
...     print(k)
bucket
checksum
filetype
key
size
version_id

This is also how files are stored inside the record in the _files key:

>>> print(record['_files'][0]['key'])
hello.txt

Extracting file from record

Some Invenio modules, e.g. Invenio-Previewer need to extract a file from record and be resilient towards exactly which record class is being used. This can done using the a record file factory:

>>> from invenio_records_files.utils import record_file_factory
>>> fileobj = record_file_factory(None, record, 'hello.txt')
>>> print(fileobj.key)
hello.txt

If a file does not exist or the record class has no files property, the factory will return None:

>>> fileobj = record_file_factory(None, record, 'invalid')
>>> fileobj is None
True