'How to create pivot tables with model_bakery
I want to create a baker recipe who create one object, objects from a pivot table and links everything well.
An incident is created when cells of a technology (2g, 3g, 4g, 5g) are down, but not all technologies are impacted. So we have a pivot table to manage this.
My goal is to avoid writing code duplicates and manage this with a simple baker.make_recipe("incidents.tests.incident_with_multiple_impacts") to create an incident and 4 different impacts (technologies are created in fixtures)
# incident/models/incident.py
class Incident(models.Model):
impacted_technologies = models.ManyToManyField(
Technology, through="IncidentIncidentImpactedTechnologies"
)
# network/models/technology.py
class Technology(models.Model):
alias = models.CharField(max_length=2, unique=True, blank=False) # ie: "2G", "3G"...
name = models.CharField(max_length=4, unique=True, blank=False)
# incident/models/incident_technologies.py
class IncidentIncidentImpactedTechnologies(models.Model):
incident = models.ForeignKey(
to="incident.Incident",
on_delete=models.CASCADE,
related_name="technology_impacts",
)
technology = models.ForeignKey(
to="network.Technology",
on_delete=models.CASCADE,
related_name="impacting_incidents",
)
During my researches, I writed code above (I want to keep first recipes to reuse them later):
from model_bakery.recipe import Recipe, foreign_key, related
def get_technology(key=None):
if key:
return Technology.objects.get_by_alias(key) # to get it with "2G"
return random.choice(Technology.objects.all())
incident = Recipe(
Incident,
# few other fields
)
impacted_techno = Recipe(
IncidentIncidentImpactedTechnologies,
incident=foreign_key(incident),
technology=get_technology, # Default, take random technology
# Fields describing a NO_IMPACT on this techno
)
impacted_techno_cell_down = impacted_techno.extend(
# Fields describing a CELL_DOWN impact on this techno
)
impacted_techno_degraded = impacted_techno.extend(
# Fields describing a DEGRADED impact on this techno
)
impacted_techno_down = impacted_techno.extend(
# Fields describing a full TECHNO_DOWN on this techno
)
# Below code is not working, because in incident, impacted_technologies is a O2M field pointing on Technology, not on IncidentIncidentImpactedTechnology.
incident_with_multiple_impacted_technos = incident.extend(
impacted_technologies=related(
impacted_techno.extend(technology=lambda: get_technology("2G")),
impacted_techno_degraded.extend(technology=lambda: get_technology("3G")),
impacted_techno_down.extend(technology=lambda: get_technology("4G")),
impacted_techno_cell_down.extend(technology=lambda: get_technology("5G")),
)
)
So how can I make a recipe create all the object tree?
Incident # Only one
| \
| [IncidentIncidentImpactedTechnology] * 4
| /
[Technology] * 4 # already created
avoiding doing this:
@staticmethod
def _get_technology(key):
return Technology.objects.get_by_natural_key(key)
def _create_incident_with_multiple_impacted_technos(self):
incident = baker.make_recipe("incident.tests.incident")
baker.make_recipe(
"incident.tests.impacted_techno",
incident=incident,
technology=self._get_technology("2G"),
)
baker.make_recipe(
"incident.tests.impacted_techno_cell_down",
incident=incident,
technology=self._get_technology("3G"),
)
baker.make_recipe(
"incident.tests.impacted_techno_degraded",
incident=incident,
technology=self._get_technology("4G"),
)
baker.make_recipe(
"incident.tests.impacted_techno_down",
incident=incident,
technology=self._get_technology("5G"),
)
return incident
def test_my_test(self):
incident = self._create_incident_with_multiple_impacted_technos()
# test stuff
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
