I am using a model class Event
that contains an optional ManyToManyField to another model class, User
(different events can have different users), with a factory class EventFactory
(using the Factory Boy library) with a serializer EventSerializer
. I believe I have followed the docs for factory-making and serializing, but am receiving the error:
ValueError: "< Event: Test Event >" needs to have a value for field "id" before this many-to-many relationship can be used.
I know that both model instances must be created in a ManyToMany before linking them, but I do not see where the adding is even happening!
Can someone clarify how to properly use a ManyToManyField using models, factory boy, and serializers in a way I am not already doing?
Here is my code:
models.py
@python_2_unicode_compatible
class Event(CommonInfoModel):
users = models.ManyToManyField(User, blank=True, related_name='events')
# other basic fields...
factories.py
class EventFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Event
@factory.post_generation
def users(self, create, extracted, **kwargs):
if not create:
# Simple build, do nothing.
return
if extracted:
# A list of users were passed in, use them
# NOTE: This does not seem to be the problem. Setting a breakpoint
# here, this part never even fires
for users in extracted:
self.users.add(users)
serializers.py
class EventSerializer(BaseModelSerializer):
serialization_title = "Event"
# UserSerializer is a very basic serializer, with no nested
# serializers
users = UserSerializer(required=False, many=True)
class Meta:
model = Event
exclude = ('id',)
test.py
class EventTest(APITestCase):
@classmethod
def setUpTestData(cls):
cls.user = User.objects.create_user(email='[email protected]',
password='password')
def test_post_create_event(self):
factory = factories.EventFactory.build()
serializer = serializers.EventSerializer(factory)
# IMPORTANT: Calling 'serializer.data' is the exact place the error occurs!
# This error does not occur when I remove the ManyToManyField
res = self.post_api_call('/event/', serializer.data)
Thank you for any help you can give!
Regarding the error:
It seems like the missing id
is due to the use of .build()
instead of .create()
(or just EventFactory()
). The former does not save the model, and therefore it does not get an id
value, whereas the latter does (refer to factory docs and model docs).
I suspect the serializer still expects the object to have an id
, even though the many-to-many relationship is optional, because it cannot enforce a potential relationship without an id
.
However, there might be a simpler solution to the actual task. The above method is a way of generating the POST data passed to post_api_call()
. If this data was instead created manually, then both the factory and serializer become unnecessary. The explicit data method might even be better from a test-perspective, because you can now see the exact data which has to produce an expected outcome. Whereas with the factory and serializer method it is much more implicit in what is actually used in the test.