6

Mam projekt AngularJS, który używał Django jako frameworka za pośrednictwem Django Rest Framework (DRF).Django Rest Framework Wiele do wielu pól związanych z samym sobą

Utworzono model grupy i skonfigurowałem dla niego klasę serializera, jednak chcę teraz ustanowić nowe pole w tym modelu o nazwie related_groups, które będzie odwoływać się do tego samego modelu (grupy) jako tablicy kluczy podstawowych .

Nie wiem, czy możliwe jest samodzielne odwoływanie się do serializera, i nie wiem, jak inaczej przejść przez powiązane grupy z front-endu, które mogą być wybrane i wybrane przez użytkowników kto jest właścicielem grupy. Chcę, aby to pole odwoływać się do kluczy podstawowych innych wierszy grupy i iterować przez ten zbiór grup, aby ustanowić pokrewną relację grupy.

class GroupSerializer(serializers.ModelSerializer): 

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

i reprezentacja wydaje się być dokładnie tym, czego chcę:

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) 

a model jest reprezentowany jako takie:

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) 

Widok dostępu do tego modelu jest dość prosty widok 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) 

który wywołuje pewne metody odkażania i walidacji:

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) 

który wzywa:

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 

który jest po prostu otoki wokół select_for_update() metoda Django.

Migracja utworzyła nową tabelę o nazwie group_related_groups, z identyfikatorem, grupą from_group i kolumną to_group, używaną przez Django jako skrzyżowanie/odnośnik do ustanowienia tych relacji.

Mogę pisać indywidualnie do tego punktu końcowego, ale serializator GroupSerializer wydaje się nie chcieć dopuszczać wielu wartości przez domyślnie.

Tak więc, przy użyciu żądania PUT do PUT wartość "2" do grupy z PK z 1 zakończy się pomyślnie. Jednak próby umieścić ['2','3'], [2,3], 2,3 i '2','3'

Śledzenie go z powrotem przez myślą o metodzie użytkowego, widzę, że to serializer.is_valid() braku żądania, tak że sprawia mi myśleć, że to many=True problem, ale don” t wiedzieć, który serial serializator relacji użyć do tego konkretnego samoodnawialnego problemu ManyToManyField.

Gdy debugowanie, mam wyprowadzania serializer.is_valid() błędów do syslog tak:

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

A ja dostaję ten komunikat wyjątku jako odpowiedź:

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

Wyjście błędu debugowania dla obj_serializer.Błąd jest

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

A oto debug messasge na request.data:

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

którym się powiedzie, a

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

która nie powiedzie się. Patrząc na to teraz, zastanawiam się, czy problemem jest formatowanie danych formularza Postman. Jeśli tak, to będę się czuł głupio.

Czy mogę reprezentować relacje wiele do wielu z modelu z powrotem do siebie z DRF, czy też potrzebuję niestandardowego serializera tylko dla tabeli relacji? Dokumentacja dla DRF nie korzysta z modeli self-referencyjnych, a wszystkie przykłady, które znajduję online, używają wielu modeli lub wielu Serializatorów.

Czy można korzystać z ManyToManyField w moim modelu, który jest samoodnoszący się przy użyciu Django Rest Framework (DRF) i serializerów? Jeśli tak to jak?

+0

proszę wyjaśnić co masz na myśli przez 'GroupSerializer() : 'in your second snippet – e4c5

+1

Gdy' serializer.is_valid() 'nie działa, jaka jest zawartość' serializer.errors'? Zwykle daje dobre wskazówki, dlaczego się nie udaje. –

+0

@ e4c5, sprawdzając relację serializera z metodą opisaną w dokumentacji relacji serializera, tutaj: http://www.django-rest-framework.org/api-guide/relations/#inspecting-relationships, a relacja wydaje się być w porządku. – Smittles

Odpowiedz

3

Wygląda jak przypadek serializacji zagnieżdżonej. Model powinien być -

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') 

a serializers -

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') 

Następnie można użyć NestedGroupSerializer dostęp do wszystkich grup związanych.

Domyślnie zagnieżdżone serializery są tylko do odczytu. Jeśli chcesz obsługiwać operacje zapisu w zagnieżdżonym serializmierze, musisz utworzyć metody create() i/lub update(), aby jawnie określić sposób zapisywania relacji podrzędnych.

Nadzieję, że pomaga.

+0

Ta metodologia kończy się niepowodzeniem, podobnie jak moja. serializer.is_valid() wraca jako false podczas przekazywania '[2,3]' lub '2,3'. Jednak doświadczenie jest takie samo, gdy podaje się pojedynczą liczbę całkowitą, taką jak '2' lub' 3', samą w sobie – Smittles

+0

Mam już pole dla grup related_groups - i tabelę utworzoną dla ManyToManyField - dlaczego więc potrzebuję "dzieci"? dlaczego potrzebuję pokrewnej nazwy, jeśli mam powiązanie ManyToManyField z tym samym modelem? – Smittles

+0

Ponadto potrzebuję więcej niż jednej powiązanej grupy, stąd użycie ManyToManyField. Korzystanie z ManyToManyField tworzy tabelę relacji w bazie danych - idealną na szybkie relacje, w których jedna grupa NightClub może mieć kilka grup DJ, gdzie NightClub ma jeden zestaw członków, a DJ 1 ma inny zestaw, a DJ 2 ma jeszcze inny zestaw. – Smittles

1

Spróbuj użyć ModelViewSet jak widzenia:

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

iw swoim urls.conf, coś jak: z widokiem na przywóz z routerami przywozowych rest_framework

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