1

django-rest-framework とネストされたオブジェクトに問題があります。

Cart オブジェクトと、Cart にリンクする CartItem があります。

class Cart(models.Model):
    customer = models.ForeignKey(Customer)
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)

class CartItem(models.Model):
    cart = models.ForeignKey(Cart, related_name='cartitems')
    product = models.ForeignKey(Product, help_text='Product in a cart')
    quantity = models.PositiveIntegerField(default=1, help_text='Quantity of this product.')
    date_added = models.DateTimeField(auto_now_add=True, help_text='Date that this product was added to the cart.')

両方のシリアライザーを作成しました:

class CartItemSerializer(serializers.ModelSerializer):
    product = serializers.HyperlinkedRelatedField(view_name='product-detail')

    class Meta:
        model = CartItem

class CartSerializer(serializers.ModelSerializer):
    customer = serializers.HyperlinkedRelatedField(view_name='customer-detail')
    cartitems = CartItemSerializer(required=False)
    total_price = serializers.CharField(source='total_price', read_only=True)
    shipping_cost = serializers.CharField(source='shipping_cost', read_only=True)

    class Meta:
        model = Cart
        fields = ('id', 'customer', 'date_created', 'date_modified', 'cartitems', 'total_price', 'shipping_cost')

ただし、新しいカートを作成するために POST を実行しようとすると、存在しない CartItem を設定しようとするとエラーが発生します。

TypeError at /api/v1/carts/
add() argument after * must be a sequence, not NoneType

ただし、Cart は実際に CartItems を持つ必要はありません。

required=FalseCart.cartitems で取得したフラグを DRF に反映させる方法はありますか?

乾杯、ビクター

編集:

私はもう一度それをたどって突き刺しました:

CartSerializer オブジェクトを使用して、rest_framework/serializers.py で BaseSerializer.save() を呼び出しています。

def save(self, **kwargs):
    """
    Save the deserialized object and return it.
    """
    if isinstance(self.object, list):
        [self.save_object(item, **kwargs) for item in self.object]

        if self.object._deleted:
            [self.delete_object(item) for item in self.object._deleted]
    else:
        self.save_object(self.object, **kwargs)

    return self.object

次に、同じクラスで save_object() を呼び出します。

def save_object(self, obj, **kwargs):
    """
    Save the deserialized object and return it.
    """
    if getattr(obj, '_nested_forward_relations', None):
        # Nested relationships need to be saved before we can save the
        # parent instance.
        for field_name, sub_object in obj._nested_forward_relations.items():
            if sub_object:
                self.save_object(sub_object)
            setattr(obj, field_name, sub_object)

    obj.save(**kwargs)

    if getattr(obj, '_m2m_data', None):
        for accessor_name, object_list in obj._m2m_data.items():
            setattr(obj, accessor_name, object_list)
        del(obj._m2m_data)

    if getattr(obj, '_related_data', None):
        for accessor_name, related in obj._related_data.items():
            if isinstance(related, RelationsList):
                # Nested reverse fk relationship
                for related_item in related:
                    fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name
                    setattr(related_item, fk_field, obj)
                    self.save_object(related_item)

                # Delete any removed objects
                if related._deleted:
                    [self.delete_object(item) for item in related._deleted]

            elif isinstance(related, models.Model):
                # Nested reverse one-one relationship
                fk_field = obj._meta.get_field_by_name(accessor_name)[0].field.name
                setattr(related, fk_field, obj)
                self.save_object(related)
            else:
                # Reverse FK or reverse one-one
                setattr(obj, accessor_name, related)
        del(obj._related_data)

Cart オブジェクトには_related_data、dict に設定されたフィールドがあります。

{'cartitems': None}

したがって、最後から 2 番目の行で、django/db/models/fields/related.py で setattr を呼び出します。

def __set__(self, instance, value):
    if instance is None:
        raise AttributeError("Manager must be accessed via instance")

    manager = self.__get__(instance)
    # If the foreign key can support nulls, then completely clear the related set.
    # Otherwise, just move the named objects into the set.
    if self.related.field.null:
        manager.clear()
    manager.add(*value)

次の原因となるのは、この最後のライナー (manager.add(*value)) です。

TypeError: add() argument after * must be a sequence, not NoneType
4

1 に答える 1

1

Serializer Relation Docsを確認すると、まずフィールドに追加many=Trueする必要がありますcartitems

残念ながら、これは読み取り専用です。ドキュメントには、「読み取りと書き込みの関係については、フラットなリレーショナル スタイルを使用する必要があります」と書かれています。これに関する質問は、ここで見つけることができます(ただし、1 対 1 のケースのみを扱っています)。

現在の戦略では、cartitems読み取り専用にしてから、何かを実行するかpost_save、2 番目のシリアライザーを使用するか、関連するエンティティを設定するために別のエンドポイントに別の要求を行う必要があります。ネストされた書き込みのサポートが改善されることを考えると、当面は別のエンドポイントへの別のリクエストに傾倒する可能性があります (ただし、それは明らかに制約に依存します)。

それが役立つことを願っています。

編集:(コメントでの質問と議論への更新後)。

CartItems を追加するために別のエンドポイントを使用している場合は、cartitems読み取り専用にするとエラーが解消されます。

ただし(読み取り専用にしない場合)、そこから投稿した DRF コードを見るとsave_objectrelated_item in relatedブロック内で実際にリストが必要であることがわかります。CartItems のない Cart の適切な dict (フラグメント) は、 not {'cartitems': None}but rather{'cartitems': []}です。— これはもちろん、required=Falseフラグが何もしていないことを意味します。(したがって、短い答えはおそらく「いいえ」です — <a href="https://groups.google.com/forum/#!topic/django-rest-framework/2iEat5mCbvY/discussion" rel="nofollow noreferrer">メーリングリストの議論に従う

于 2013-08-27T08:44:29.437 に答える