webentwicklung-frage-antwort-db.com.de

django Rest Framework erstellen verschachtelte Objekte "Models" von POST

Ich versuche POST ein neues verschachteltes Objekt. Das Problem besteht einfach darin, das Top-Objekt (Playlist) zu erstellen, aber nicht das "ChannelItem" ...

Meine Modelle:

class Playlist(models.Model):
    provider = models.IntegerField()
    channel_id = models.CharField(max_length=100)
    channel_version = models.CharField(blank=True, max_length=100)
    start = models.DateTimeField()
    url = models.CharField(max_length=500)


class ChannelItem(models.Model):
    playlist = models.ForeignKey(Playlist, editable=False, related_name='channelitems')
    content_id = models.CharField(max_length=100)
    content_version = models.CharField(blank=True, max_length=100)

Mein Serializer:

class ChannelItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChannelItem
        fields = ('content_id', 'content_version')
        exclude = ('id')
        depth = 1


class PlaylistSerializer(serializers.ModelSerializer):

    class Meta:
        model = Playlist
        fields = ('id', 'provider', 'channel_id', 'channel_version', 'start', 
                  'url', 'channelitems')
        depth = 2

channelitems = ChannelItemSerializer()

Ich verwende die Curl, um folgende Daten zu posten:

'{"provider":125,"channel_id":"xyz", "channel_version":"xsqt", 
"start":"2012-12-17T11:04:35","url":"http://192.168.1.83:8080/maaaaa",
"channelitems":[{"content_id":"0.flv", "content_version":"ss"},
{"content_id":"1.flv","content_version":"ss"}]}' http://localhost:8000/playlist_scheduler/playlists/

Ich erhalte die Nachricht:

HTTP/1.1 201 CREATED
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 17 Dec 2012 20:12:54 GMT
Server: 0.0.0.0

{"id": 25, "provider": 125, "channel_id": "xyz", "channel_version": "xsqt",
"start":"2012-12-17T11:04:35", "url": "http://localhost:8080/something",
"channelitems": []}
25
skopp

Geschachtelte Darstellungen unterstützen derzeit kein Lese Schreibzugriff -/und sollten stattdessen schreibgeschützt sein.

Sie sollten sich stattdessen eher mit einer flachen Darstellung befassen, indem Sie pk oder Hyperlinks verwenden.

Wenn Sie die verschachtelte Darstellung benötigen, können Sie zwei separate Endpunkte in Betracht ziehen - einen flachen, schreibbaren Endpunkt und einen geschachtelten schreibgeschützten Endpunkt.

21
Tom Christie

Wenn jemand eine schnelle und schmutzige Lösung dafür braucht, habe ich mir diese Lösung ausgedacht, die ich vorübergehend in einem Projekt verwenden werde:

class NestedManyToManyField(serializers.WritableField):
    def to_native(self, value):
        serializer = self.Meta.serializer(value.all(), many=True, context=self.context)
        return serializer.data
    def from_native(self, data):
        serializer = self.Meta.serializer(data=data, many=True, context=self.context)
        serializer.is_valid()
        serializer.save()
        return serializer.object
    class Meta:
        serializer = None

Dann erstellen Sie Ihre eigene Unterklasse von NestedManyToManyField:

class TopicNestedSerializer(NestedManyToManyField):
    class Meta:
        serializer = MyOriginalSerializer

Ein Beispiel für MyOriginalSerializer:

class MyOriginalSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.MyModel
        fields = ('id', 'title',)

Das funktioniert bisher gut für mich. Seien Sie sich jedoch bewusst, dass es saubere Lösungen gibt

6
Micha Mazaheri

nach langem bemühen habe ich eine erste version gemacht, die funcinasse ....__ Ich glaube, dass mit etwas verbesserung der ModelSerializer aufgenommen werden könnte

class ChannelItemSerializer(serializers.ModelSerializer):

    class Meta:
        model = ChannelItem
        fields = ('id', 'content_id', 'content_version')

    def field_from_native(self, data, files, field_name, into):
        try:
            if self._use_files:
                _files = files[field_name]
            else:
                _data = data[field_name]
        except KeyError:
            if getattr(self, 'default', None):
                _data = self.default
            else:
                if getattr(self, 'required', None):
                    raise ValidationError(self.error_messages['required'])
                return

        if type(_data) is list:
            into[field_name] = [] 
            for item in _data:
                into[field_name].append(self._custom_from_native(item))
        else:
            into[field_name] = self._custom_from_native(_data)


    def _custom_from_native(self, data):
        self._errors = {}
        if data is not None:
            attrs = self.restore_fields(data, None)
            attrs = self.perform_validation(attrs)
        else:
            self._errors['non_field_errors'] = ['No input provided']

        if not self._errors:
            return self.restore_object(attrs, instance=getattr(self, 'object', None))




class PlaylistSerializer(serializers.ModelSerializer):

    class Meta:
        model = Playlist
        fields = ('id', 'provider', 'channel_id', 'channel_version', 'start', 'url', 'channel_items')
        depth = 1

    channel_items = ChannelItemSerializer()

    def restore_object(self, attrs, instance=None):
        self.foreign_data = {}

        for (obj, model) in self.opts.model._meta.get_all_related_objects_with_model():
            field_name = obj.field.related_query_name()
            if field_name in attrs:
                self.foreign_data[field_name] = attrs.pop(field_name)


        return super(PlaylistSerializer, self).restore_object(attrs, instance)

    def save(self, save_m2m=True):
        super(PlaylistSerializer, self).save(save_m2m)

        if getattr(self, 'foreign_data', None):
            for accessor_name, object_list in self.foreign_data.items():
                setattr(self.object, accessor_name, object_list)
            self.foreign_data = {}

        return self.object
3
skopp

Für mich gibt es einen hybriden Workaround, mit dem ich OK bin. Erstellen Sie eine Ansicht, die Folgendes aufweist:

  • das Feld ManyToMany in seiner nicht verschachtelten Form des Serialisierers
  • alias ​​das verschachtelte ManyToMany-Feld in eine Variable mit _objs als Suffix und als schreibgeschützt angeben
  • wenn Sie PUT zurück zum Server bringen, stimmen Sie die beiden Aliasfelder ab und speichern Sie das Ergebnis im Feld "Ungeschachtelte Serialisierer"

z.B.

class MSerializer(serializers.HyperlinkedModelSerializer):
    foo_objs = TempSensorSerializer(source='foos', many=True, allow_add_remove=True,required=False,read_only=True)
    class Meta:
        model = M
        fields = ('url', 'foos', 'foo_objs')

Ich liebe diese Lösung nicht, aber es ist besser als der Versuch, die verschachtelten Felder nach dem Abrufen des ursprünglichen Containers separat abzufragen und zu sortieren.

1
Ross Rogers