UPDATE #1
Added Repository structure
We are using Django's ORM portion of the framework.
We have 1 Shared app, and a couple of Apps, without relationship between themselves. the Shared app defines an abstract BaseModel that all other models from all other apps inherit from, along with a few shared models, that may be referenced as a foreign key in the other apps.
We are using a monorepo, so no different repositories or anything like that.
These models can be used in various applications, from (non django) web apps, and to processing units that are deployed when needed and gets destroyed when done with their processing.
I'm trying to understand if there's a single place we can call
import django
django.setup()
and remove the need to call it in the top of every app that imports one of the models, and that will comply with both of our unittests and service run.
This is the shared models.py file
import json
import uuid
from datetime import datetime
import common.db.enums as shared_enums
from django.db import models
from django.db.models import CASCADE
MAX_VARCHAR_LENGTH = 255
def generate_uuid():
return uuid.uuid4().hex
class UUIDString(models.UUIDField): # pragma: no cover
def from_db_value(self, value, expression, connection):
if isinstance(value, uuid.UUID):
return value.hex
else:
return value
class BaseModel(models.Model): # pragma: no cover
id = UUIDString(primary_key=True, default=generate_uuid, editable=False, unique=True, db_index=True)
created_on = models.DateTimeField(default=datetime.now)
updated_on = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class Customer(BaseModel): # pragma: no cover
customer_guid = UUIDString(max_length=MAX_VARCHAR_LENGTH, db_index=True, unique=True)
class FlowInstance(BaseModel): # pragma: no cover
name = models.CharField(max_length=MAX_VARCHAR_LENGTH, null=False)
customer = models.ForeignKey(Customer, on_delete=CASCADE, db_index=True)
flow_type = models.IntegerField(null=True, choices=shared_enums.FlowType.get_choices())
created_by = models.CharField(max_length=MAX_VARCHAR_LENGTH, null=True)
class FlowRun(BaseModel): # pragma: no cover
flow_instance = models.ForeignKey(FlowInstance, on_delete=CASCADE, db_index=True)
status = models.IntegerField(null=True, choices=shared_enums.ExecutionStatus.get_choices())
start_time = models.DateTimeField(null=True)
finish_time = models.DateTimeField(null=True)
class FlowRunResults(BaseModel): # pragma: no cover
flow_run = models.OneToOneField(FlowRun, on_delete=CASCADE, db_index=True)
errors = models.TextField(null=True)
output_location = models.TextField(null=True)
And this is one of the apps using the shared models.py
from common.db.shared.models import BaseModel, FlowInstance, FlowRun, UUIDString, MAX_VARCHAR_LENGTH
from django.db import models
from django.db.models.deletion import CASCADE, DO_NOTHING
class Action(BaseModel): # pragma: no cover
action_id = models.CharField(max_length=MAX_VARCHAR_LENGTH, null=True, db_index=True)
action_name = models.CharField(max_length=MAX_VARCHAR_LENGTH, null=True)
class DataSetDetails(BaseModel): # pragma: no cover
action = models.OneToOneField(Action, on_delete=DO_NOTHING, db_index=True, null=False)
frequency = models.IntegerField(null=True)
identified_frequency = models.IntegerField(null=True)
class RunDataSet(BaseModel): # pragma: no cover
dataset_details = models.ForeignKey(DataSetDetails, null=False, on_delete=CASCADE)
flow_run = models.ForeignKey(FlowRun, on_delete=DO_NOTHING, null=True)
storage_path = models.TextField(null=True)
class DataCollection(BaseModel): # pragma: no cover
name = models.CharField(max_length=MAX_VARCHAR_LENGTH, null=False)
target = models.OneToOneField(DataSetDetails, on_delete=DO_NOTHING, null=False, related_name="target")
related = models.OneToOneField(DataSetDetails, on_delete=DO_NOTHING, null=True, related_name="related")
metadata = models.OneToOneField(DataSetDetails, on_delete=DO_NOTHING, null=True, related_name="metadata")
flow_instance = models.OneToOneField(FlowInstance, on_delete=DO_NOTHING, null=False)
Our repository structure is as follows:
├── common/ <-- Modules here should be self sustainable, but can have dependencies on other commons
├── aws/
├── db/
└── shared
├── migrations/
├── __init__.py
├── manage.py
├── models.py
└── apps.py
├── theme1/ <-- contain many apps related to theme1.
├── common/
├── theme1db/
├── migrations/
├── __init__.py
├── manage.py
├── models.py
└── apps.py
├── app1/
└── app2/
├── theme2/ <-- contain many apps related to theme2
├── common/
└── theme2db/
├── migrations/
├── __init__.py
├── manage.py
├── models.py
└── apps.py
├── service1/
└── service2/
The structure is that every directory is basically sort of it's own repo.
each directory which isn't the root /common, can only reference modules from the root /common, or common modules from inside the theme
e.g. app1, can only reference modules from theme1/app1, /theme1/common or /common