I might not be adding much, but I got this error while working on unit tests for a rest api with no forms at all, so I wanted to provide an example of how I fixed it in this scenario. I'm working with Django 4.1.7 and Django Rest Framework 3.14.0.
This is a Pokemon cards project, so there's a PokemonType model (that represents types as "fire", "water", "grass", etc. and their resistances and weaknesses):
class PokemonType(models.Model):
name = models.CharField(max_length=100, blank=False)
strong_vs = models.ManyToManyField('self', symmetrical=False, related_name="strong_versus")
weak_vs = models.ManyToManyField('self', symmetrical=False, related_name="weak_versus")
resistant_to = models.ManyToManyField('self', symmetrical=False, related_name="resists")
vulnerable_to = models.ManyToManyField('self', symmetrical=False, related_name="vulnerable")
I wanted to write a simple test that verifies a PokemonType can be created, but this test causes the Direct assignment to the forward side of a many-to-many set is prohibited
error:
class PokemonTypeModelTests(TestCase):
def test_create_pokemontype(self):
pokemon_type = PokemonType.objects.create(
name = 'type 1',
strong_vs = [],
weak_vs = [2],
resistant_to = [3, 4],
vulnerable_to = [2, 5]
)
pokemon_type_from_db = PokemonType.objects.get(id=pokemon_type.id)
self.assertEqual(pokemon_type_from_db.name, 'type 1')
One could think that creating the object and then adding the relationships like this might help:
pokemon_type = PokemonType.objects.create(name='type 1')
pokemon_type.resistant_to.set([3, 4])
but this raises error django.db.utils.IntegrityError: The row in table 'api_pokemontype_resistant_to' with primary key '1' has an invalid foreign key: api_pokemontype_resistant_to.to_pokemontype_id contains a value '3' that does not have a corresponding value in api_pokemontype.id.
This happens because the PokemonType objects with ids 3 and 4 don't exist yet (keep in mind that Django creates a special database for the purpose of testing). The many-to-many relationship is actually implemented as a separate table in the database, and Django needs the objects to actually exist in the database before being able to add relationships between them.
That's why a solution in this case was to first create all the objects, add them to the database and then use set()
to create the many-to-many relationships:
def test_create_pokemontype(self):
# this is the "main" object:
pokemon_type = PokemonType.objects.create(name='type 1')
# these are only created for the relationship fields:
type2 = PokemonType.objects.create(name='type 2')
type3 = PokemonType.objects.create(name='type 3')
type4 = PokemonType.objects.create(name='type 4')
type5 = PokemonType.objects.create(name='type 5')
# now we can "set" these objects in each ManyToManyField:
pokemon_type.strong_vs.set([])
pokemon_type.weak_vs.set([type2])
pokemon_type.resistant_to.set([type3, type4])
pokemon_type.vulnerable_to.set([type2, type5])
# and perform assertions with them:
pokemon_type_from_db = PokemonType.objects.get(id=pokemon_type.id)
self.assertEqual(pokemon_type_from_db.name, 'type 1')
self.assertEqual(set(pokemon_type_from_db.strong_vs.all()), set())
self.assertEqual(set(pokemon_type_from_db.weak_vs.all()), {type2})
self.assertEqual(set(pokemon_type_from_db.resistant_to.all()), {type3, type4})
self.assertEqual(set(pokemon_type_from_db.vulnerable_to.all()), {type2, type5})