Writing Migrations

Adding a New Table

Before writing the Django migration, you must first modify the following files:

datamodel.py

Add your new table and relationships. Follow the capitalization style used in the existing tables closely.
Each new table increments the tableId by 1. Tables exclusive to Specify 7 start at TableID 1000.

Follow how the id field is capitalized by other tables, otherwise it will show up on schema config.

models.py

Add class to models.py (specifyweb\specify\models.py for most tables, you may put it in a different app if your table fits better there).
Generally only the first character is capitalized in the class name, but there are some exceptions (probably unintentional). Just keep it consistent with the name you put in models_by_table_id.py later.

types.ts

In: components/DataModel/types.ts
Read the comment to regenerate the schema.

models_by_table_id.py

Add the table name to model_names_by_table_id and model_names_by_app.
Make sure the capitalization matches what you used on models.py.

  • Note: Attachment tables are identified by having exactly “attachment” at the end, no capital.

You may now proceed onto generating the migration.

Generating New Migrations

Generate the Django migration to create the table (generated from the specification in models.py).
Run the following command (assuming you’re using docker):
docker exec specify7-specify7-1 ve/bin/python manage.py makemigrations

Navigate to the newly created file and add extra operations if you need to.
Edit the generated migration to allow for reverting if you added any additional functionality.

Additional functionality includes adding the new table to the schema config, which you probably want.
To add the new table’s fields to the schema config, add something similar to the following:

MIGRATION_0007_TABLES = [
    ('SpDataSetAttachment', 'An attachment temporarily associated with a Specify Data Set for use in a WorkBench upload.')
]

MIGRATION_0007_FIELDS = {
    'Spdataset': ['spDataSetAttachments']
}

def apply_migration(apps, schema_editor):
    # Update Schema config
    Discipline = apps.get_model('specify', 'Discipline')
    for discipline in Discipline.objects.all(): # New SpDataSetAttachment table
        for table, desc in MIGRATION_0007_TABLES:
            update_table_schema_config_with_defaults(table, discipline.id, desc, apps)
    for discipline in Discipline.objects.all(): # New relationship Spdataset -> SpDataSetAttachment
        for table, fields in MIGRATION_0007_FIELDS.items():
            for field in fields: 
                update_table_field_schema_config_with_defaults(table, discipline.id, field, apps)

The following function reverses it:

def revert_migration(apps, schema_editor):
    # Revert Schema config changes
    for table, _ in MIGRATION_0007_TABLES: # Remove SpDataSetAttachment table
        revert_table_schema_config(table, apps)
    for table, fields in MIGRATION_0007_FIELDS.items(): # Remove relationship Spdataset -> SpDataSetAttachment
            for field in fields: 
                revert_table_field_schema_config(table, field, apps)

This is handled by adding these functions to the operations at the bottom of the file

migrations.RunPython(apply_migration, revert_migration, atomic=True)

Update datamodel.json

Update datamodel.json.
Regenerate by going to http://localhost/context/datamodel.json

Update tests

These front-end tests may now fail:
\specify7\specifyweb\frontend\js_src\lib\components\DataModel\__tests__\resource.test.ts

Follow the instructions in the comment to regenerate uniqueFields.json.

/**
 * If this test breaks, uniqueFields.json needs to be regenerated.
 * 1. Go to the dev console on the browser
 * 2. Run the function _getUniqueFields()
 * 3. Paste the text into uniqueFields.json and format with prettier
 */

Update front-end test snapshots to fix the rest of the tests:
npm run unitTests -- -u

Run tests

Run the front-end tests to make sure everything works.
npm t