Reputation: 375
I need to create a set of models like this:
class Step(models.Model):
field1 = models.CharField(max_length=50)
field2 = models.CharField(max_length=50)
class Scenario(models.Model):
name = models.CharField(max_length=50)
steps = models.ManyToManyField(Step, related_name="scenarios")
The thing is I want the scenario to include several steps in an order that I will specify and even the same step several times. Like this:
Scenario1:
step1
step2
step1
step3
I want this step's order to be easy editable in admin site.
I found out about filter_horizontal
, it looks a lot like what I need in admin site, but it has no option to add a step one more time and to move steps up and down.
Upvotes: 1
Views: 615
Reputation: 477607
There is a saying in computer science that says that: "All problems in computer science can be solved by another level of indirection" - David Wheeler.
In that case you should use a "through" table, like:
class Step(models.Model):
field1 = models.CharField(max_length=50)
field2 = models.CharField(max_length=50)
class Scenario(models.Model):
name = models.CharField(max_length=50)
steps = models.ManyToManyField(
Step,
through='ScenarioStep',
related_name="scenarios"
)
class ScenarioStep(models.Model):
scenario = models.ForeignKey(
'Scenario',
on_delete=models.CASCADE,
related_name='scenariostep'
)
step = models.ForeignKey(
'Step'
on_delete=models.CASCADE,
related_name='scenariostep'
)
order = models.PositiveIntegerField()
class Meta:
ordering = ['order']
Here we thus introduce an extra model that replaces the hidden "through table" that was already constructed by the ManyToManyField
.
A Scenario
thus has a Step
, given there is a ScenarioStep
record, and we order this by an Order
field.
We can, for example, add the given steps to the scenario
with:
s1 = Step.objects.create(field1='step1')
s2 = Step.objects.create(field1='step1')
s3 = Step.objects.create(field1='step1')
sc1 = Scenario.objects.create(name='Scenario1')
ScenarioStep.objects.create(order=1, scenario=sc1, step=s1)
ScenarioStep.objects.create(order=2, scenario=sc1, step=s2)
ScenarioStep.objects.create(order=3, scenario=sc1, step=s1)
ScenarioStep.objects.create(order=4, scenario=sc1, step=s3)
We can then iterate over the steps with:
for step in sc1.steps.order_by('scenariostep'):
# ... (do something with the step)
pass
Upvotes: 1