Вопрос

I'm trying to find an elegant solution to this problem encountered using the Django REST framework. We have a parent model with a child object and a one to one relationship. Our requirements are that the child object is optional, can be null and can also be patched to null from a previously existing value. Additionally, if the parent object is deleted the child object should be as well.

This simple set-up that recreates the problem:

class ChildSerializer(serializers.ModelSerializer):
    class Meta:
        model = Child


class ParentSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Parent

    parent_value = serializers.CharField(required=False, max_length=1024)
    child = ChildSerializer(read_only=False, required=False, many=False)


class Parent(models.Model):
    class Meta:
        app_label = "testparent"

    parent_value = models.CharField(max_length=1024, null=True, blank=True)


class Child(models.Model):
    class Meta:
        app_label = "testparent"

    child_value = models.CharField(max_length=1024, null=True, blank=True)
    parent = models.ForeignKey(Parent, null=True, blank=True, related_name="child")


class ParentViewSet(viewsets.ModelViewSet):
    permission_classes = (AllowAny,)

    queryset = Parent.objects.all()
    serializer_class = ParentSerializer

This works:

{
    "parent_value": "abc", 
    "child": { "child_value": "something" }
}

This gets an error:

{
    "parent_value": "abc", 
    "child": null
}

Note that the child object is optional and by default the REST framework populates the field with null.

I'm using Django v.1.5.4 and REST framework v.2.3.13

The error:

Traceback:
File "{site-packages}/django/core/handlers/base.py" in get_response
  115.                         response = callback(request, *callback_args, **callback_kwargs)
File "{site-packages}/rest_framework/viewsets.py" in view
  78.             return self.dispatch(request, *args, **kwargs)
File "{site-packages}/django/views/decorators/csrf.py" in wrapped_view
  77.         return view_func(*args, **kwargs)
File "{site-packages}/rest_framework/views.py" in dispatch
  400.             response = self.handle_exception(exc)
File "{site-packages}/rest_framework/views.py" in dispatch
  397.             response = handler(request, *args, **kwargs)
File "{site-packages}/rest_framework/mixins.py" in create
  54.             self.object = serializer.save(force_insert=True)
File "{site-packages}/rest_framework/serializers.py" in save
  596.             self.save_object(self.object, **kwargs)
File "{site-packages}/rest_framework/serializers.py" in save_object
  1023.                     setattr(obj, accessor_name, related)
File "{site-packages}/django/db/models/fields/related.py" in __set__
  474.         manager.add(*value)


Exception Value: add() argument after * must be a sequence, not NoneType

The workaround we're currently using is to have a OneToOne field for parent and then use a post_delete signal handler to delete the child object. This feels very hacky. We've in general have encountered a few issues with trying to manipulate nested objects and are wondering if we're going about it wrong or if maybe in this case we've encountered a bug in either django or the rest framework.

Thanks!

Это было полезно?

Решение

While this question was asked a year ago, Django Rest Framework has released a major update (3.1.1) since then which simplifies and enhances how the framework deals with nested objects.

Your requirements:

  1. One to One relationship - In the parent Serializer definition, you can specify the paramter many=False.

  2. Delete a Child Object when a Parent is deleted - In the Parent Serializer, implement the perform_destroy function. In this function, delete the Parent instance and Child instance.

Here is the DRF Serializer documentation

Другие советы

Have you tried setting allow_null = True in the ChildSerializer?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top