相关对象序列化器Django restframework

时间:2018-07-09 14:26:00

标签: django serialization django-models django-rest-framework

我的问题与this one有点不同。我有一个与此模型相似的模型:

class Project(models.Model):
    project_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
    created_by_id = models.ForeignKey('auth.User', related_name='project', on_delete=models.SET_NULL, blank=True, null=True)
    created_by = models.CharField(max_length=255, default="unknown")
    created = models.DateTimeField(auto_now_add=True)

使用以下序列化器:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = serializers.ReadOnlyField(source='created_by_id.username')

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created')

和相应的视图:

class projectsView(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def perform_create(self, serializer):
        serializer.save(created_by_id=self.request.user)

此代码的行为与我想要的一样,但是会强制信息冗余,并且不会利用基础的关系数据库。我试图使用链接问题中的信息来实现“在数据库中写入用户ID,但在平面json中返回“ get”中的用户名”,但没有成功:

删除模型中的“ created_by”字段。将序列化器替换为:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User


class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', write_only=True)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

哪个不会100%给我我想要的东西,即用平面json中的用户名替换用户ID,但返回类似{'project_id': <uuid>, 'created_by': <user json object>, 'created': <data>}的信息。但是我仍然遇到{'created_by_id': ['This field is required.']} 400错误。

问题:如何从request.user信息中将用户ID写入数据库对象,以引用实际的用户ID,但在projectView端点的GET请求中返回简单的用户名,而无需在模型中明确存储用户名?或更笼统地说,如何使用默认的序列化DRF功能和默认的DRF视图mixins将数据库对象(Django模型)序列化为客户json响应?

问题的替代表达:如何在模型中存储对另一个DB记录的ID引用(可以在不由有效负载提供的情况下访问它),但是在序列化器级别反序列化从该对象引用派生的信息,例如作为被引用对象的一个​​特定字段?

2 个答案:

答案 0 :(得分:0)

我建议您将两个不同的序列化器用于GetPOST操作。将您的serializers.py更改为

class ProjectGetSerializer(serializers.ModelSerializer):
    created_by_id = serializers.StringRelatedField()

    class Meta:
        model = Project
        fields = '__all__'


class ProjectCreateSerializer(serializers.ModelSerializer):
    created_by_id = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())

    def create(self, validated_data):
        return Project.objects.create(**validated_data, created_by=validated_data['created_by_id'].username)

    class Meta:
        model = Project
        fields = '__all__'


另外,如果您正在寻找ModelViewSet,我建议将CRUD operations用于API类。因此,视图将是这样,

class projectsView(viewsets.ModelViewSet):
    queryset = Project.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return ProjectCreateSerializer
        return ProjectGetSerializer


因此,创建Project的有效载荷是,

{
}

您在创建Project用户时必须记住的一件事必须登录

更新-1
serializer.py

class ProjectCreateSerializer(serializers.ModelSerializer):
    created_by_id = serializers.StringRelatedField()

    class Meta:
        model = Project
        fields = '__all__'

    def create(self, validated_data):
        return Project.objects.create(**validated_data, created_by_id=self.context['request'].user)

views.py

class projectsView(viewsets.ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectCreateSerializer

答案 1 :(得分:0)

该错误位于write_only字段选项中。 required参数的默认值设置为True,但如果我们看一下模型,其目的是使其不为必需。在此视图中,我将perform_create用作后期处理以保存在Model DB表示形式中。由于创建级别的required默认值为True,因此数据库的第一个.save()失败。由于这纯粹是内部逻辑,因此不需要。因此,只需在required=False上添加PrimaryKeyRelatedField选项即可完成工作:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User


class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', write_only=True, required=False)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

如果我坚持纯粹在序列化器级别进行反序列化,那么在模型级别也必须执行require = True才能覆盖序列化器的.save函数。可能还有一种方法可以在序列化程序中获取用户ref,以使视图实现更加“默认” ...这可以通过使用Jerin中的默认值来完成:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', 
                           write_only=True,
                           required=False,
                           default=serializers.CurrentUserDefault())

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

现在仅使用用户名来平整json,您需要使用一个Slug字段而不是UserSerializer:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = serializers.SlugRelatedField(
      queryset=User.objects.all(), slug_field="username")
    created_by_id = serializers.PrimaryKeyRelatedField(
      queryset=User.objects.all(), source='created_by', write_only=True, required=False)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

然后只有用户模型的用户名字段值将显示在get有效负载上的create_by json标签上。

更新-1

经过一些调整之后,我得出了最终版本:

class ProjectSerializer(serializers.ModelSerializer):

    created_by_id = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(), write_only=True, required=False, default=serializers.CurrentUserDefault())
    created_by = serializers.SerializerMethodField('creator')

    def creator(self, obj):
        return obj.created_by_id.username

    class Meta:
        model = Project
        fields = ('project_id', 'created_by_id', 'created_by', 'created')