6

Ich habe ein AngularJS-Projekt, das Django als Framework über das Django Rest Framework (DRF) verwendet.Django Rest Framework Viele zu viele Feld bezogen auf sich selbst

Ich habe ein Gruppenmodell erstellt und eine Serialisierungsklasse dafür eingerichtet, möchte jedoch jetzt ein neues Feld für dieses Modell namens related_groups einrichten, das auf dasselbe Modell (Gruppe) verweist wie ein Array von Primärschlüsseln .

Ich weiß nicht, ob es möglich ist, im Serialisierer selbst zu referenzieren, und ich weiß nicht, wie sonst verwandte Gruppen vom Front-End, die von den Benutzern ausgewählt und ausgewählt werden können Wer besitzt die Gruppe? Ich möchte, dass dieses Feld die Primärschlüssel anderer Gruppenzeilen referenziert und durch diese Sammlung von Gruppen iteriert, um eine verwandte Gruppenbeziehung herzustellen.

class GroupSerializer(serializers.ModelSerializer): 

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

und die Darstellung erscheint genau das zu sein, was ich will:

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) 

und das Modell wird als solche dargestellt werden:

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) 

Der Blick dieses Modell den Zugriff auf ziemlich einfache CRUD Ansicht ist :

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

denen einige Hygienisierung und Validierungsmethoden ruft:

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) 

die Anrufe:

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 

die nur ein Wrapper um select_for_update() Django Methode.

Die Migration erstellte eine neue Tabelle namens group_related_groups mit einer ID, einer from_group- und einer to_group-Spalte, die von Django als Junction/Lookup verwendet wird, um diese Beziehungen herzustellen.

Ich kann einzeln auf diesen Endpunkt schreiben, aber der Serializer GroupSerializer scheint standardmäßig nicht mehrere Werte zulassen zu wollen.

Es ist also erfolgreich, eine PUT-Anforderung zum PUT zu verwenden, um einen Wert von '2' für die Gruppierung mit einem PK von 1 zu erhalten. Allerdings versucht ['2','3'], [2,3], 2,3 und '2','3'

es zurück durch den Blick auf eine Hilfsmethode zu setzen Tracing, ich sehe, dass es serializer.is_valid() die Anforderung fehlgeschlagen ist, so dass macht mich denke, es ist ein many=True Thema, aber ich don‘ Sie wissen, welche Beziehung Serializer für dieses spezielle selbstreferenzielle ManyToManyField-Problem verwenden soll.

Beim Debuggen, ich bin zum Ausgeben des serializer.is_valid() Fehler wie diese syslog:

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

Und ich bin immer diese Ausnahme Meldung als Antwort:

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

Die Debugfehlerausgabe für obj_serializer.Fehler ist

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

Und hier ist debug messasge auf request.data:

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

, die erfolgreich ist, und

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

was fehlschlägt. Wenn ich mir das jetzt anschaue, frage ich mich, ob Postman-Formulardaten das Problem darstellen. Wenn das der Fall ist, werde ich mich ziemlich dumm fühlen.

Kann ich viele-zu-viele-Beziehungen von einem Modell zu sich selbst mit DRF darstellen, oder muss ich einen benutzerdefinierten Serializer nur für die Beziehungstabelle haben? Die Dokumentation für DRF verwendet keine selbstreferentiellen Modelle und alle Beispiele, die ich online finde, verwenden entweder mehrere Modelle oder mehrere Serialisierer.

Ist es möglich, ManyToManyField in meinem Modell zu verwenden, das mit dem Django Rest Framework (DRF) und seinen Serialisierern selbstreferenziell ist? Wenn das so ist, wie?

+0

bitte erklären, was Sie 'GroupSerializer bedeuten() : 'in Ihrem zweiten Ausschnitt – e4c5

+1

Sobald' serializer.is_valid() 'fehlgeschlagen ist, was ist der Inhalt von' serializer.errors'? Es gibt normalerweise gute Hinweise, warum es versagt. –

+0

@ e4c5, Überprüfung der Serializer-Beziehung mit der Methode beschrieben in der Serializer Relationen Dokumentation, hier: http://www.django-rest-framework.org/api-guide/relations/#inspecting-relationships, und die Beziehung scheint zu geordnet sein. – Smittles

Antwort

3

Sieht wie ein Fall verschachtelter Serialisierung aus. Das Modell sollte -

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

und die Serializer -

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

Sie dann NestedGroupSerializer können alle damit verbundenen Gruppen zuzugreifen.

Standardmäßig sind verschachtelte Serializer schreibgeschützt. Wenn Sie Schreiboperationen in einem geschachtelten Serializer-Feld unterstützen möchten, müssen Sie create() - und/oder update() -Methoden erstellen, um explizit anzugeben, wie die untergeordneten Beziehungen gespeichert werden sollen.

Hoffe, dass hilft.

+0

Diese Methode schlägt auf Gültigkeit, wie meins. serializer.is_valid() kommt als falsch zurück, wenn "[2,3]" oder "2,3" übergeben wird. Die Erfahrung ist jedoch die gleiche, wenn ich eine einzelne Ganzzahl wie '2' oder' 3' selbst übergebe. – Smittles

+0

Ich habe bereits ein Feld für related_groups - und eine Tabelle, die für das ManyToManyField erstellt wurde - also warum brauche ich 'children'? Warum benötige ich einen related_name, wenn ich ein ManyToManyField-Element mit demselben Modell verknüpft habe? – Smittles

+0

Außerdem muss ich mehr als eine verwandte Gruppe haben, daher die Verwendung von ManyToManyField. Durch die Verwendung von ManyToManyField wird eine Beziehungstabelle in der Datenbank erstellt - ideal für schnelle Beziehungen, wenn eine NightClub-Gruppe mehrere DJ-Gruppen hat, NightClub eine Gruppe von Mitgliedern hat und DJ 1 eine andere Gruppe hat, und DJ 2 hat noch eine andere Gruppe. – Smittles

1

Versuchen ModelViewSet als Ansicht mit:

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

und in Ihrem urls.conf, so etwas wie: von Import Ansichten von rest_framework Import Router

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