I have following model (campaigns/models.py)
from django.db import models
from django.contrib.auth.models import User
class Module(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class TestType(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class Test(models.Model):
PROTO_CHOICES = (
('TCP','tcp'),
('UDP','udp'),
)
module = models.ForeignKey(Module)
testtype = models.ForeignKey(TestType)
name = models.CharField(max_length=50, unique=True)
port = models.IntegerField(blank=True, null=True)
proto = models.CharField(max_length=3, blank=True, choices=PROTO_CHOICES)
payload = models.TextField()
sig_match = models.TextField(blank=True)
def __unicode__(self):
return self.name
class Campaign(models.Model):
name = models.CharField(max_length=50, unique=True)
tests = models.ManyToManyField(Test)
updated = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, editable=False)
def __unicode__(self):
return self.name
class CampaignRun(models.Model):
campaign = models.ForeignKey(Campaign)
name = models.CharField(max_length=50, unique=True)
run_start = models.DateTimeField()
run_end = models.DateTimeField()
updated = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, editable=False)
class Meta:
def __unicode__(self):
return self.name
class TestRun(models.Model):
campaignrun = models.ForeignKey(CampaignRun)
test = models.ForeignKey(Test)
test_start = models.DateTimeField()
test_end = models.DateTimeField()
alert = models.TextField(blank=True)
flag = models.IntegerField(blank=True, null=True)
…which resulted in following database (sqlite3):
+-----------------------------+
| |
+---------------------------+ +------------------------------+ | +--------------------+ |
| campaigns_testrun | | campaigns_test | | | campaigns_module | |
+----------------+----------+ +----------------+-------------+ | +------+-------------+ |
| id | INT | +--| id | INT |--+ +--| id | INT | |
+--| campaignrun_id | INT | | | module_id | INT |-----+ | name | VARCHAR(20) | |
| | test_id | INT |--+ | testtype_id | INT |--+ +------+-------------+ |
| | test_start | DATETIME | | name | VARCHAR(50) | | |
| | test_end | DATETIME | | port | INT | | +--------------------+ |
| | alert | TEXT | | proto | VARCHAR(3) | | | campaigns_testtype | |
| | flag | INT | | payload | TEXT | | +------+-------------+ |
| +----------------+----------+ | sig_match | TEXT | +-----| id | INT | |
| +----------------+-------------+ | name | VARCHAR(20) | |
| +------+-------------+ |
| |
| +------------------------+ +--------------------------+ |
| | campaigns_campaignrun | +--------------------+ | campaigns_campaign_tests | |
| +-------------+----------+ | campaigns_campaign | +-------------+------------+ |
+--| id | INT | +------+-------------+ | id | INT | |
| campaign_id | INT |-----| id | INT |-----| campaign_id | INT | |
| name | INT | | name | VARCHAR(50) | | test_id | INT |------------+
| run_start | DATETIME | +------+-------------+ +-------------+------------+
| run_end | DATETIME |
+-------------+----------+
Here is the logic:
- A campaign is composed of tests
- A campaign can be run many times (campaignrun) and results in test results (test run).
- For each campaign run, tests related information are gathered from the test table
Obviously, there is a circular relationship and the integrity of the database is only ensured by the application.
However, from a database architect perspective, this circular relation is incorrect since it can results in integrity problems:
- In SQL directly or from the django admin module, it is possible to create tests in campaigns_testrun that are NOT in the campaign_tests table
For the above reason, the solution would be to link campaigns_testrun with campaigns_campaign_tests directly. However, I don’t think it is possible in Django.
Should you have any idea how to deal with this, please be welcomed to comment.
Thank you in advance for your help.
Use ManyToManyField through argument.
This allows you to use a custom model, and refer to it in FKs, M2M, etc …
Also note that you can quote your class names, ie. ForeignKey(‘SomeClass’), enabling circular relations without problem.
Welcome to Django, enjoy your ride B)