6

我有一个AngularJS项目,它通过Django Rest Framework(DRF)将Django用作框架。Django Rest框架与自己相关的多对多的领域

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

虽然我不知道是否可以在序列化程序中自我引用,但我不知道如何从前端通过相关组,从而可以由用户挑选和选择谁拥有该组织。我希望该字段引用其他Group行的主键,并遍历该组的集合以建立相关的组关系。

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用作联结/查找来建立这些关系。

我可以单独写入该端点,但序列化程序GroupSerializer似乎不希望允许通过默认多个值。

因此,使用PUT请求将PUT值设为'2'以PK为1组是成功的。然而,试图将['2','3'][2,3]2,3'2','3'

跟踪回通过以实用的方法,我看到它serializer.is_valid()失败的请求,这样让我觉得这是一个many=True问题,但我不t知道哪个关系序列化器用于这个特定的自引用ManyToManyField问题。

调试时,我输出serializer.is_valid()错误,系统日志是这样的:

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

而且我得到这个异常消息作为响应:

Message: "Attempt to serialize <class 'bioapi.models.Group'> with id 1 failed with errors"

obj_serializer的调试错误输出。错误是

obj_serializer.error of {'related_groups': ['Incorrect type. Expected pk value, received str.']}

而这里的调试讯息支付上request.data:

{'group_name': 'Default Guest Group', 'related_groups': [1], 'group_id': 2, 'category': 'guest'}

其成功,

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

失败。现在看这个,我想知道Postman表单数据格式是否是问题。如果是这种情况,我会觉得很愚蠢。

我可以使用DRF表示从模型到模型本身的多对多关系,还是仅需要为关系表使用自定义序列化程序? DRF文档不使用自参考模型,我在网上找到的所有示例都使用多个模型或多个串行器。

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

+0

请解释一下你的'GroupSerializer的意思() :'在你的第二个片段中 – e4c5

+1

'serializer.is_valid()'失败后,'serializer.errors'的内容是什么?它通常给出很好的提示,为什么它失败。 –

+0

@ e4c5,使用序列化程序关系文档中描述的方法检查序列化程序的关系,这里:http://www.django-rest-framework.org/api-guide/relations/#inspecting-relationships,并且关系似乎是保持秩序。 – Smittles

回答

3

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

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()方法,以明确指定应如何保存子关系。

希望有所帮助。

+0

这种方法在有效性方面失败,我的方法也是如此。当通过'[2,3]'或'2,3'时,serializer.is_valid()返回为false。然而,当传递一个像'2'或'3'这样的整数时,体验是一样的 – Smittles

+0

我已经在这里为related_groups创建了一个字段 - 为ManyToManyField创建了一个表 - 为什么我需要'children'?为什么我需要一个related_name如果我有一个ManyToManyField相关的回到相同的模型? – Smittles

+0

此外,我需要有多个相关的组,因此ManyToManyField的用法。使用ManyToManyField可以在数据库中创建一个关系表 - 对于快速关系的匹配来说非常适合,其中一个NightClub小组可能有几个DJ小组,其中NightClub有一组成员,DJ 1有另一组成员,DJ 2有另一组成员。 – Smittles

1

尝试使用ModelViewSet的观点:

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

,并在您urls.conf,是这样的:从rest_framework进口路由器 进口观点

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