How To: Describe your Data Model
The wq framework’s default project template provides a simple survey schema with Category and Observation models, but this is designed to be replaced with a custom schema. wq is extremely flexible to adapt to a variety of project workflows, but you may need to think a bit about how to structure your data before continuing. In the simplest case, you will have a single web form that populates a single database table. More advanced cases will have multiple inter-related tables, some filled in at the same time and others filled out separately. wq makes it easy to start simple and add more complexity later.
wq uses the Django model syntax as the primary way to define a data schema. After installing wq and starting a project via
wq create, you’ll have a Django project (under db/myproject) and a separate Django application folder (under db/myproject_survey). This Django application contains three key files:
models.pyis used to tell Django how to create the database table(s) corresponding to your schema.
rest.pyregisters the same model definition with the wq.db REST API so that records can be retrieved and updated from the client application.
serializers.pydefines how database fields are mapped from the database to the REST API, and configures how they are rendered in the UI. (
serializers.pyis not required if the default mapping and configuration is sufficient)
There are three ways to create a new model definition. The list of available field/question types is listed here.
Option 1: XLSForm syntax
With this option, you can configure all of your field/question definitions in a spreadsheet following the XLSForm standard used by Open Data Kit, Survey123, and related projects. You can then have wq generate the Django application and templates from the spreadsheet. To create an XLSForm, you can use an online form builder like the one provided by KoboToolbox, or you can just download an example spreadsheet and add the definitions manually. Note that not every feature of XLSForm is supported - but it is easy to implement custom input types with similar functionality. Once you have an XLSForm ready you can use the built-in
wq addform command provided by
wq.create. For best results, use a relatively short name for the file and run the command in your
cd [PROJECTNAME]/db wq addform ~/survey.xlsx
You should see a new folder,
survey/, with the files
rest.py, and (depending on your data model
Option 2: Django Model syntax
You can create a Django application folder manually (or by copying myproject_survey) and define
models.py via Django model classes. You will then want to create a
rest.py file that registers each model class with the wq.db router. You may also need to define a custom serializer in
serializers.py, for example to support nested forms or custom input types.
# survey/models.py from django.db import models class Survey(models.Model): date = models.DateField() # ...
# survey/rest.py from wq.db import rest from .models import Survey rest.router.register_model( Survey, fields="__all__", )
Option 3: SQL Syntax
Finally, if you are more comfortable with SQL (or have an existing database) you can define the tables there and generate an initial
models.py by running python manage.py inspectdb.
Creating the Database Tables
If you use option 2 or 3 above, edit your project’s
settings.py to ensure the new application folder is listed under
INSTALLED_APPS. This should happen automatically if you use
# myproject/settings.py INSTALLED_APPS = [ # ... 'wq.db.rest', 'wq.db.rest.auth', # Project apps 'survey' ]
Then, run Django’s built in migration commands to create database tables in PostgreSQL corresponding to your model classes. For above example, the following should work:
python manage.py makemigrations survey python manage.py migrate
After the commands complete, you can use
python manage.py dbshell, psql, or pgAdmin to confirm that the tables are present. If all goes well, you should also be able to open a browser and visit your website’s /config.json and /modelnames.json to confirm that the model(s) are registered.