I'm trying to write a test to check that the create method of my Viewset
works ok:
@pytest.mark.django_db
def test_create(admin_user):
parent = factories.ParentFactory()
payload = {
'name': 'child',
'parent_id': parent.pk,
}
view = views.ParentViewSet.as_view({'post': 'create'})
request = factory.post('/', payload, format='json')
force_authenticate(request, user=admin_user)
response = view(request)
assert response.status_code == status.HTTP_201_CREATED
assert models.Child.objects.count() == 1
child = models.Child.objects.first()
assert child.name == 'child'
However, I get the following error when I run the code:
psycopg2.errors.NotNullViolation: null value in column "parent_id" violates not-null constraint
But the test for the Parent
create method runs fine:
def test_create(admin_user):
payload = {
'name': 'parent',
'children': [
{
'name': 'child',
}
],
}
view = views.ParentViewSet.as_view({'post': 'create'})
request = factory.post('/', payload, format='json')
force_authenticate(request, user=admin_user)
response = view(request)
assert response.status_code == status.HTTP_201_CREATED
assert models.Parent.objects.count() == 1
season = models.Parent.objects.first()
assert season.name == 'parent'
assert season.children.count() == 1
Can someone tell me the correct way to write the payload for the child test create?
Edit:
serializers.py
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = models.Child
fields = ['id', 'name']
class ParentSerializer(serializers.ModelSerializer):
children = ChildSerializer(many=True)
class Meta:
model = models.Parent
fields = ['id', 'name', 'children']
def create(self, validated_data):
children = validated_data.pop('children')
parent = super().create(validated_data)
for child in children:
child['parent'] = parent
self.fields['children'].create(children)
return parent
views.py
class ParentViewSet(
viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
):
queryset = models.Parent.objects.all().prefetch_related(
'children'
)
serializer_class = serializers.ParentSerializer
class ChildViewSet(
viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
):
queryset = models.Child.objects.all().prefetch_related(
'children'
)
serializer_class = serializers.ChildSerializer
I think you have a field "parent" in the child model, but as you are not including that field in ChildSerializer, even if you send it in the request body, it is not included while creating a Child instance, hence the error you're getting. You have several ways to solve it
1 - You can add parent field to ChildSerializer:
class ChildSerializer(serializers.ModelSerializer):
class Meta:
model = models.Child
fields = ['id', 'name', 'parent']
and send the request like this:
payload = {
'name': 'child',
'parent': parent.pk,
}
2 - If you do not want that, you may keep the ChildSerializer as it is now, and add parent only while saving the child instance, with something like this:
class ChildViewSet(
...
):
queryset = models.Child.objects.all().prefetch_related('children')
serializer_class = serializers.ChildSerializer
def perform_create(self, serializer):
serializer.save(parent_id=self.request.data.get('parent'))
Again you'd need to send the request like this:
payload = {
'name': 'child',
'parent': parent.pk,
}
But in this second scenario, you're not including parent field in request validation, and if a client does not send parent field or sends an invalid value there, you'll get an exception. If you are to go with this solution, I'd advise to somehow validate parent field as well.