2016-04-18 17 views
0

Angenommen, ich habe zwei Modelle:ManyToMany Beziehung benutzerdefinierte Serializer

class IPAddress(models.Model): 
    address = models.CharField() 

class Rule(models.Model): 
    name = models.CharField() 
    ips = models.ManyToMany(IPAddress) 

ich in der Lage möchte, wie unten eine Regel durch eine Anfrage hinzuzufügen:

{ 
"name":"Foo", 
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"] 
} 

Auch ich will ip in jede Anforderung konstruieren (es gibt keine uRL eine IP direkt zu konstruieren) für neue Regel, so habe ich eine Klasse für Manager wie folgt geschrieben:

class RuleManager(models.Manager): 
    def create(self, validated_data): 
     rule = Rule(name=validate_data['name']) 
     rule.save() 

     rule.ips = [IPAddress.objects.get_or_create(item.lower()) for item in validated_data['ips']] 

B ut in Serializer konnte ich keinen richtigen Weg finden, dies zu zeigen, ich habe einen Serializer wie folgt geschrieben:

class RuleSerializer(serializers.Serializer): 
    name = serializers.CharField() 
    ips = serializers.SlugRelatedField(many=True, slug_field='address', validators=[], queryset=models.IPAddress.objects.all()) 

Aber das Problem ist es die IP-in Anfrage bestätigt, und wenn es nicht so IP ist es einen Fehler zurückgibt, obwohl ich die Validatoren eine leere Liste gesetzt habe.

Ich habe zwei Fragen, wie kann ich diese Validierung deaktivieren? Und ist mein Weg, um die Serializer für das Schreiben und das Modell für mein Szenario geeignet ist (ich konnte die Anforderung nicht ich ändern und die Antwort, die ich senden muss)

+0

Sie benötigen die verschachtelten Serializer für die Erstellung der 'ips' über API oder Sie benötigen es nur zum Lesen, wo ips bereits erstellt werden. – AKS

Antwort

2

Wenn Sie eine Instanz von Rule in folgendem Format zurück:

{ 
"name":"Foo", 
"ips":["192.168.1.40", "4.4.4.4", "8.8.8.8"] 
} 

Sie könnten eine RuleSerializer erstellen und SlugRelatedField verwenden.

SlugRelatedField funktioniert nur mit den Objekten, die bereits vorhanden sind. Da Sie Objekte als auch die Schaffung würden, könnten Sie die to_internal_value Implementierung ändern, um die Objekte zu erstellen, die nicht existiert (referenced from here):

class CreatableSlugRelatedField(serializers.SlugRelatedField): 

    def to_internal_value(self, data): 
     try: 
      return self.get_queryset().get_or_create(**{self.slug_field: data})[0] 
     except ObjectDoesNotExist: 
      self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data)) 
     except (TypeError, ValueError): 
      self.fail('invalid') 

class RuleSerializer(serializers.ModelSerializer): 

    ips = serializers.CreatableSlugRelatedField(
     many=True, 
     slug_field='address' # this is the ip address 
     queryset=IPAddress.objects.all() 
    ) 

    class Meta: 
     model = Rule 
     fields: ('name', 'ips') 

UPDATE: Basierend auf den Kommentaren in der Frage:

ich die Anfrage nicht ich, ich muss erhalten und die Antwort ändern könnte senden

Aber wenn Sie dann verschachtelten Serializer obwohl Ihre Darstellung leicht verwenden könnten, würde ändern müssen:

{ 
    "name": "Foo", 
    "ips": [ 
     {"address": "192.168.1.40"}, 
     {"address": "4.4.4.4"}, 
     {"address": "8.8.8.8"} 
    ] 
} 

und dann die verschachtelten Serializer (more documentation here):

class IPAddressSerializer(serializers.ModelSerializer): 

    class Meta: 
     model = IPAddress 
     fields: ('address',) 

class RuleSerializer(serializers.ModelSerializer): 

    ips = IPAddressSerializer(many=True) 

    class Meta: 
     model = Rule 
     fields: ('name', 'ips') 
+0

Es scheint wie ein Hack, weil wir 'ips' read_only Feld setzen, aber wir verwenden es zum Schreiben, auch wenn wir es als' read_only' setzen, wird es aus valided_data entfernt, Sie können dies überprüfen, indem Sie 'print (valided_data)' in hinzufügen das Betteln der 'create' Funktion. Außerdem habe ich viele verschachtelte Relationen mit jedem, um seine Daten auf niedrigerer Ebene zu konstruieren, daher denke ich, dass es angenehmer wäre, 'ModelManager' zu verwenden, um Daten auf niedrigerer Ebene zu konstruieren, anstatt' create' aus der Serializer-Klasse zu verwenden. – MohsenTamiz

+0

Nun, das ist kein Hack, aber was ist [von DRF empfohlen] (http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers). Obwohl du recht damit hast, 'read_only' zu verwenden.Es ist hier nicht erforderlich, also können Sie es in Ihrer Implementierung entfernen. Was das Schreiben von "create" im Manager angeht, hängt das ganz von Ihren Anforderungen ab. – AKS

+0

Wenn Sie das 'read_only'-Feld entfernen, tritt das Validierungsproblem auf, wie ich in meiner Frage angegeben habe, weil es standardmäßig überprüft, ob die 'ip' existiert oder nicht. – MohsenTamiz

Verwandte Themen