-
Notifications
You must be signed in to change notification settings - Fork 8
Models
Django uses models, managed by an object-relational mapper (ORM), to allow one to interface with a database without needing to write queries manually. This document specifies how the Malasakit database is structured.
Models are set up as Python classes and are analogous to tables in SQL. Each model contains fields, which are class attributes that are analogous to columns in a table. A model instance is analogous to a row in a table.
To reuse common fields and behavior between models, Malasakit uses abstract inheritance exclusively. Abstract models cannot be instantiated and have no tables of their own, but they pass down their fields to child models.
Django's ORM needs to interface with an underlying database implementation such as SQLite. However, the database is not automatically synchronized with the model definitions in the source code. Making changes to the Python source, such as adding a new field, does not automatically restructure the database. Django provides migration tools for addressing this issue.
Migrations essentially allow one to record how the database structure changes over time.
In Django, migrations are implemented as Python source files in the migrations
directory of every application.
These sources can be picked up by version control and shared.
When the model definitions are changed, the makemigrations
command of manage.py
should be invoked.
This command scans the source tree to look for what changes have been made since the last migration, and determines what tables and fields need to be altered, and how.
Django then writes migration files to the migrations
directory of the targeted application specifying what operations should be performed.
While makemigrations
is intelligent, it will not be able to handle complex changes, such as moving data in one model into two separate ones.
Therefore, one may need to manually edit the latest migration source at this point to obtain the desired database state.
Generally speaking, operations such as adding new models, adding new fields, and altering exiting fields do not require intervention.
Finally, use the migrate
of manage.py
to apply unapplied migrations to the current database.
Migration files should never be changed retroactively because doing so could result in an inconsistent state. Instead, create new migrations to roll back unwanted changes.
As discussed, Django uses models to provide database-agnostic persistent storage. This means that, regardless of one's underlying database implementation, such as SQLite or MySQL, the behavior of each model from Python should remain the same.
However, the choice of database implementation is still important:
- For local development, the Malasakit project uses SQLite, which is a lightweight SQL implementation that operates on files. There are no passwords, accounts, or other complex configuration hurdles. Furthermore, data written to user files are easy to share and export for others to use.
- In production, MySQL, which is a more robust implementation of SQL, is preferred. MySQL, unlike SQLite, allows for concurrent writes, which is critical in production.
The core of the database consists of concrete models derived from the abstract Question
and Response
models.
Generally speaking, there is a one-to-one correspondence between question and response models:
- The
QuantitativeQuestion
model is paired with theQuantitativeQuestionRating
model. - The
QualitativeQuestion
model is paired with theComment
model. - The
OptionQuestion
model is paired with theOptionQuestionChoice
model.
Each of these pairings allow for the collection of different types of data:
- Quantitative questions collect numeric data (typically gauging sentiment on a numerical scale).
- Qualitative questions collect unstructured text responses.
- Option questions collect selections from sets of items (not necessarily ordered like a quantitative question would imply).
One could also easily extend the Question
and Response
base models to allow for other types of data to be collected.
Note that each question-response model pair should be created for a type of data collected, not for a particular input element.
For instance, ranges and numeric text box inputs can both collect quantitative question data, so creating SliderQuestion
and NumberQuestion
models would be redundant.
Instead, questions define an input_type
field that indicates how that question should be rendered.