Table Tiers¶
To define a DataJoint table in Python:
- Define a class inheriting from the appropriate DataJoint class:
dj.Lookup,dj.Manual,dj.Importedordj.Computed. - Decorate the class with the schema object (see schema)
- Define the class property
definitionto define the table heading.
DataJoint for Python is implemented through the use of classes providing access to the
actual tables stored on the database. Since only a single table exists on the database
for any class, interactions with all instances of the class are equivalent. As such,
most methods can be called on the classes themselves rather than on an object, for
convenience. Whether calling a DataJoint method on a class or on an instance, the
result will only depend on or apply to the corresponding table. All of the basic
functionality of DataJoint is built to operate on the classes themselves, even when
called on an instance. For example, calling Person.insert(...) (on the class) and
Person.insert(...) (on an instance) both have the identical effect of inserting data
into the table on the database server. DataJoint does not prevent a user from working
with instances, but the workflow is complete without the need for instantiation. It is
up to the user whether to implement additional functionality as class methods or
methods called on instances.
Manual Tables¶
The following code defines two manual tables, Animal and Session:
@schema
class Animal(dj.Manual):
definition = """
# information about animal
animal_id : int # animal id assigned by the lab
---
-> Species
date_of_birth=null : date # YYYY-MM-DD optional
sex='' : enum('M', 'F', '') # leave empty if unspecified
"""
@schema
class Session(dj.Manual):
definition = """
# Experiment Session
-> Animal
session : smallint # session number for the animal
---
session_datetime : datetime # YYYY-MM-DD HH:MM:SS
session_start_time : float # seconds relative to session_datetime
session_end_time : float # seconds relative to session_datetime
-> [nullable] User
"""
Note that the notation to permit null entries differs for attributes versus foreign key references.
Lookup Tables¶
Lookup tables are commonly populated from their contents property.
The table below is declared as a lookup table with its contents property provided to generate entities.
@schema
class User(dj.Lookup):
definition = """
# users in the lab
username : varchar(20) # user in the lab
---
first_name : varchar(20) # user first name
last_name : varchar(20) # user last name
"""
contents = [
['cajal', 'Santiago', 'Cajal'],
['hubel', 'David', 'Hubel'],
['wiesel', 'Torsten', 'Wiesel']
]
@schema
class ProcessingParamSet(dj.Lookup):
definition = """ # Parameter set used for processing of calcium imaging data
paramset_idx: smallint
---
-> ProcessingMethod
paramset_desc: varchar(128)
param_set_hash: uuid
unique index (param_set_hash) (1)
params: longblob # dictionary of all applicable parameters
"""
- This syntax enforces uniqueness of a secondary attribute.
Imported and Computed Tables¶
Imported and Computed tables provide make methods to determine how
they are populated, either from files or other tables.
Imagine that there is a table test.Image that contains 2D grayscale images in its
image attribute. We can define the Computed table, test.FilteredImage that filters
the image in some way and saves the result in its filtered_image attribute.
@schema
class FilteredImage(dj.Computed):
definition = """ # Filtered image
-> Image
---
filtered_image : longblob
"""
def make(self, key):
img = (test.Image & key).fetch1('image')
key['filtered_image'] = my_filter(img)
self.insert1(key)
Part Tables¶
The following code defines a Imported table with an associated part table. In Python,
the master-part relationship is expressed by making the part a nested class of the
master. The part is subclassed from dj.Part and does not need the @schema
decorator.
@schema
class Scan(dj.Imported):
definition = """
# Two-photon imaging scan
-> Session
scan : smallint # scan number within the session
---
-> Lens
laser_wavelength : decimal(5,1) # um
laser_power : decimal(4,1) # mW
"""
class ScanField(dj.Part):
definition = """
-> master
ROI: longblob # Region of interest
"""
def make(self, key):
... # (1)
self.insert1(key)
self.ScanField.insert1(ROI_information)
- This make method is truncated for the sake of brevity. For more detailed examples, please visit Element Calcium Imaging table definitions