首页 文章

Django Rest框架许多对很多领域都与自身相关

提问于
浏览
7

我有一个AngularJS项目,它通过Django Rest Framework(DRF)使用Django作为框架 .

我已经创建了一个Group模型并为它设置了一个序列化器类,但是我想在该模型上 Build 一个名为 related_groups 的新字段,该字段将引用与主键数组相同的模型(Group) .

我不知道是否可以在序列化程序中进行自引用,而且我不知道如何通过前端传递相关组,这可以由拥有该组的用户选择和选择 . 我希望该字段引用其他组行的主键,并遍历该组集合以 Build 相关的组关系 .

class GroupSerializer(serializers.ModelSerializer):

class Meta:
    model = mod.Group
    fields = (
        'group_id',
        'group_name',
        'category',
        'related_groups',
    )

表示似乎正是我想要的:

GroupSerializer():
    group_id = IntegerField(read_only=True)
    group_name = CharField(max_length=100)
    category = CharField(max_length=256, required=False
    related_groups = PrimaryKeyRelatedField(many=True, queryset=Group.objects.all(), required=False)

并且模型表示如下:

class Group(models.Model):
    """
    Model definition of a Group. Groups are a collection of users (i.e.
    Contacts) that share access to a collection of objects (e.g. Shipments).
    """
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length=100)
    owner_id = models.ForeignKey('Owner', related_name='groups')
    category = models.CharField(max_length=256)
    related_groups = models.ManyToManyField('self', blank=True, null=True)
    history = HistoricalRecords()

    def __unicode__(self):
        return u'%s' % (self.group_name)

    def __str__(self):
        return '%s' % (self.group_name)

访问此模型的视图非常简单CRUD视图:

@api_view(['GET', 'PUT', 'DELETE'])
@authentication_classes((SessionAuthentication, BasicAuthentication))
@permission_classes((IsAuthenticated, HasGroupAccess))
def group_detail(request, pk, format=None):
    group, error = utils.get_by_pk(pk, mod.Group, request.user)
    if error is not None:
        return error
    if request.method == 'GET':
        serializer = ser.GroupSerializer(group)
        return Response(serializer.data)
    elif request.method == 'PUT':
        return utils.update_object_by_pk(request, pk, mod.Group,
                                         ser.GroupSerializer)
    elif request.method == 'DELETE':
        return utils.delete_object_by_pk(request.user, pk, mod.Group)

这称为一些消毒和验证方法:

def update_object_by_pk(request, pk, obj_type, serializer):
    try:
        with transaction.atomic():
            obj, error = select_for_update_by_pk(pk, obj_type, request.user)
            if error is not None:
                return error
            obj_serializer = serializer(obj, data=request.data)

            if obj_serializer.is_valid():
                obj_serializer.save()
            else:
                response = ("Attempt to serialize {} with id {} failed "
                            "with errors {}").format(str(obj_type), str(pk),
                                                     str(serializer.errors))
                return Response(response, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        response = ("Error attempting to update {} with ID={}. user={}, "
                    "error={}".format(str(obj_type), str(pk),
                                      str(request.user.email), str(e)))
        return Response(response, status=status.HTTP_400_BAD_REQUEST)
    else:
        resp_str = ("Successfully updated {} with ID={}".format(str(obj_type),
                                                                str(pk)))
        return Response(resp_str, status=status.HTTP_200_OK)

哪个叫:

def select_for_update_by_pk(pk, mod_type, user):
    response = None
    obj = None
    try:
        obj = mod_type.objects.select_for_update().get(pk=pk)
    except mod_type.DoesNotExist:
        resp_str = ("{} could not be found with ID={}.".
                    format(str(mod_type), str(pk)))
        response = Response(resp_str, status=status.HTTP_404_NOT_FOUND)
    return obj, response

这只是 select_for_update() Django方法的包装 .

迁移创建了一个名为group_related_groups的新表,其中包含ID,from_group和to_group列,Django将其用作 Build 这些关系的联结/查找 .

我可以单独写入该 endpoints ,但是串行器GroupSerializer似乎不希望默认允许多个值 .

因此,使用PUT请求PUT将'2'的值设置为PK为1的组是成功的 . 但是,试图放 ['2','3'][2,3]2,3'2','3'

通过视图追溯到实用程序方法,我看到它是 serializer.is_valid() 请求失败,所以这让我认为这是一个 many=True 问题,但我不知道哪个关系序列化程序用于这个特定的自引用ManyToManyField问题 .

在调试时,我将serializer.is_valid()错误输出到syslog,如下所示:

response = ("Attempt to serialize {} with id {} failed "
                    "with errors {}").format(str(obj_type), str(pk),
                                             str(serializer.errors))
        logger.exception(response)

我收到此异常消息作为响应:

消息:“尝试序列化<class'cioapi.models.Group'>,其中ID为1,但出现错误”

obj_serializer.error的调试错误输出是

{'related_groups'的obj_serializer.error:['不正确的类型 . 预期的pk值,收到str . ']}

这是request.data上的debug messasge:

{'group_name':'默认访客群','related_groups':[1],'group_id':2,'类别':'访客'}

哪个成功了,

<QueryDict:{'group_name':['requestomatic'],'related_groups':['2,2'],类别':['guest']}>

哪个失败了 . 现在看看这个,我想知道Postman表格数据格式是否是问题 . 如果是这样的话,我会觉得很愚蠢 .

我是否能够通过DRF表示从模型到自身的多对多关系,或者我是否需要为关系表提供自定义序列化器? DRF的文档不使用自引用模型,我在网上找到的所有示例都使用多个模型或多个序列化程序 .

是否可以在我的模型中使用ManyToManyField,它是使用Django Rest Framework(DRF)及其序列化器自引用的?如果是这样,怎么样?

2 回答

  • 1

    看起来像嵌套序列化的情况 . 该模型应该是 -

    class Group(models.Model):
      group_id = models.IntegerField(read_only=True)
      group_name = models.CharField(max_length=100)
      category = models.CharField(max_length=256, required=False)
      related_groups = models.ForeignKey('self', required=False, related_name='children')
    

    和序列化器 -

    class GroupSerializer(serializers.ModelSerializer):
        class Meta:
            model = Group
            fields = (
                'group_id', 'group_name', 'category', 'related_groups',
            )
    
    class NestedGroupSerializer(serializers.ModelSerializer):
        children = GroupSerializer(many=True, read_only=True)
    
        class Meta:
            model = Group
            fields = ('group_id', 'group_name', 'category', 'related_groups', 'children')
    

    然后,您可以使用NestedGroupSerializer访问所有相关组 .

    默认情况下,嵌套序列化程序是只读的 . 如果要支持对嵌套序列化程序字段的写操作,则需要创建create()和/或update()方法,以便明确指定应如何保存子关系 .

    希望有所帮助 .

  • 3

    尝试使用ModelViewSet作为视图:

    class GroupViewSet(viewsets.ModelViewSet):
        queryset = models.Group.objects.all()
        serializer_class = serializers.GroupSerializer
        authentication_classes = (SessionAuthentication, BasicAuthentication)
        permission_classes = (IsAuthenticated, HasGroupAccess)
    

    在你的_372615中,类似于:来自rest_framework导入路由器的导入视图

    router = routers.SimpleRouter()
    router.register(r'group', views.GroupViewset)
    urlpatterns = router.urls
    

相关问题