If you are new to Django, first please read their excellent tutorial. It will explain much of the Product Registry's implementation.
Clone this repository and
cdinto it.Install the uv Python package manager:
curl -LsSf https://astral.sh/uv/install.sh | shInstall the JSON command-line JSON processor jq.
Install the project's Python dependencies.
uv sync --dev
Activate the Python virtual environment.
source .venv/bin/activate
This project uses SQLite as the database for local development. To build a local SQLite Product Registry database,
Update the Django migrations to match the Django model definitions.
No changes detectedmeans that they are up-to-date.python manage.py makemigrations
Apply the migrations to the database to create all the tables.
python manage.py migrate
Now that the database is built, we will populate it using Jupyter Notebooks.
In another terminal window, start a Jupyter Notebook server by running
uv run jupyter lab .The Django app
ob_taxonomydefines tables for storing the Orange Button Taxonomy as database metadata that is referenced frequently in the Product Registry's code. Populate these tables by running the notebookob_taxonomy/upload_taxonomy.ipynb(note that jq must be installed).In
server/data_upsert, there are multiple Jupyter Notebooks for cleaning and uploading CEC data. Run each of these notebooks to output cleaned CEC data:- For
ProdBattery, run the notebookserver/data_upsert/ProdBattery/clean.ipynb. - For
ProdModule, run the notebookserver/data_upsert/ProdModule/clean.ipynb.
- For
With the cleaned data, upload the the CEC data.
- For
ProdBattery, run the notebookserver/data_upsert/ProdBattery/upsert_no_orm.ipynb. - For
ProdModule, run the notebookserver/data_upsert/ProdModule/upsert_no_orm.ipynb.
- For
Start a development server by running
python manage.py runserverThis project is set up to be deployed on Amazon (AWS) Elastic Container Service (ECS) with a MySQL database, where "Container" refers to a Docker container. Configuring AWS deployment infrastructure though their developer console is tedious and difficult to do consistently, so this project uses Terraform to automate the configuration instead.
These are roughly the steps to follow. Depending on what infrastructure already exists (e.g., HTTPS certificates for the domain), you may need to do additional steps.
- In
terraform/variables.tf, edit the variablesservice-name,service-name-alphanumeric, andservice-domain-name. - In
terraform/bastion.tf, edit theaws_security_groupcalledbastion_sgby writing your IP address in theingresscidr_block. This "Bastion" EC2 server is needed later for connecting to the database to upload data. - In
terraform/database.tf, edit thepasswordfield of theaws_db_instancecalledmysql. cdintoterraform.- Create an SSH key pair for connecting to the "Bastion" EC2 instance later:
ssh-keygen -f bastion_key. - Run
terraform init. - Use the AWS command-line interface to log into AWS by running
aws login. - Run
terraform planto see what infrastructure will be configured (additions, changes, and deletions). - Run
terraform applyto perform the infrastructure configuration. - Note the final outputs of
terraform apply. They include the following:- An Amazon Elastic Container Registry (ECR) address to which we will push a Docker image of this project.
- The IP address of the "Bastion" EC2 instance we will use a proxy to connect to the database.
Pushing a Docker image to Amazon Elastic Container Registry (ECR)
Rename
product_registry/settings_deployment.pytoproduct_registry/settings.pyand replace the appropriate values (e.g.,SECRET_KEY,ALLOWED_HOSTS, etc.).Build the Docker image.
docker build -t django-ecs . # django-ecs is an arbitrary name for the image
Debugging the image. With Django's HTTPS redirection turned off, try
docker run --rm -p 8000:8000 --name django-test django-ecs --bind 0.0.0.0:8000
and then go to
127.0.0.1.Log Docker into AWS.
aws ecr get-login-password --region us-<REGION>-1 | docker login --username AWS --password-stdin <AWS_ACCOUNT_NUMBER>.dkr.ecr.us-<REGION>-1.amazonaws.com
Note
If you used
sudo dockerto build the image, you must also usesudo dockerhere.Tag the Docker image.
docker tag django-ecs:latest <AWS_ACCOUNT_NUMBER>.dkr.ecr.us-<REGION>-1.amazonaws.com/<ECR_REPO_NAME>:latest
Push the Docker image to AWS ECR.
docker push <AWS_ACCOUNT_NUMBER>.dkr.ecr.us-<REGION>-1.amazonaws.com/<ECR_REPO_NAME>:latest
Look the load balancer page in the AWS developer console to find the ECS task and check whether the task instances are running. Look at the logs in AWS CloudWatch to debug.
Once an ECS task instance is running, it will automatically create the tables in the database.
If you are building an image to replace the current one in production, AWS ECS will not automatically use the newly built image if it has the same tag (e.g., "latest") as the old image. To get AWS ECS to use the new image, navigate to the ECS service following breadcrumbs like
Amazon Elastic Container Service > Clusters > ob-product-registry-2026-02-cluster > Services > ob-product-registry-2026-02-service > Health
In the top right, there should be a button labeled "Update service". Click the dropdown next to it and select "Force new deployment".
The database is in a private subnet of the virtual private cloud (VPC) we created, so we cannot connect to it directly to upload data. Instead, we use a temporary "Bastion" EC2 instance as a proxy. Run
ssh -i bastion_key -L 3307:<DATABASE_URL>:3306 ec2-user@<BASTION_EC2_IP>where we intentionally linked port 3307 of our local machine to port 3306 of the database.
You can find the DATABASE_URL in the AWS Relational Database Service section of the AWS developer console.
Now, we can connect to the database by running
mysql -h 127.0.0.1 -P 3307 -u admin -pand entering the password in terraform/database.tf.
To upload data into the database, one successful technique is as follows:
Build a local SQLite database containing all the data to be uploaded to the remote production MySQL database.
Use DuckDB to connect to both the local SQLite database than the remote database.
attach 'db.sqlite3' as lds (type sqlite); attach 'host=localhost user=admin password=<DATABASE_PASSWORD> database=OBProductRegistry port=3307' as rds (type mysql);
In DuckDB, select data from the local database to upsert into the remote database. Here is an example of inserting
ProdBatteryandProdModuledata:begin transaction; insert into rds.server_dcinput select * from lds.server_dcinput; insert into rds.server_dcoutput select * from lds.server_dcoutput; insert into rds.server_dimension select * from lds.server_dimension; insert into rds.server_product select * from lds.server_product; insert into rds.server_prodbattery select * from lds.server_prodbattery; insert into rds.server_entity select * from lds.server_entity; insert into rds.server_certificationagency select * from lds.server_certificationagency; insert into rds.server_checksum select * from lds.server_checksum; insert into rds.server_firmware select * from lds.server_firmware; insert into rds.server_prodcertification select * from lds.server_prodcertification; insert into rds.server_product_ProdCertifications select * from lds.server_product_ProdCertifications; insert into rds.server_prodcell select * from lds.server_prodcell; insert into rds.server_prodglazing select * from lds.server_prodglazing; insert into rds.server_moduleelectrating select * from lds.server_moduleelectrating; insert into rds.server_prodmodule select * from lds.server_prodmodule; insert into rds.server_prodmodule_ModuleElectRatings select * from lds.server_prodmodule_ModuleElectRatings; insert into rds.server_sourcecountry select * from lds.server_sourcecountry; insert into rds.server_product_SourceCountries select * from lds.server_product_SourceCountries; commit;
Once all the data is uploaded, destroy the "Bastion" EC2 instance to save costs. In
terraform/bastion.tf, comment out everything except theaws_security_groupnamedbastion_sg. Interraform/security_groups.tf, removeaws_security_group.bastion_sg.idfrom thesecurity_groupsofingressof theaws_security_groupnamedrds_sg. Runterraform applyto remove the security group from the database and destroy the "Bastion" EC2 instance. Next, interraform/bastion.tf, comment out theaws_security_groupnamedbastion_sg. Rerunterraform applyto destroy the Bastion's security group.
Enter the domain and the load balancer's URL into your domain provider's DNS CNAME table.