【问题标题】:How create a model resource and a related many to many resource on a single POST?如何在单个 POST 上创建模型资源和相关的多对多资源?
【发布时间】:2013-01-08 19:25:07
【问题描述】:

您好,我在一个项目中使用tastepie来公开一个rest api,并且卡在如何实现资源的端点上,所以这是我的问题:

我有以下型号,

class Product(models.Model):
    # fields...

class Order(models.Model):
    products = models.ManyToManyField('Product', through='OrderProduct')
    # other fields...

class OrderProduct(models.Model):
    order      = models.ForeignKey('Order')
    product    = models.ForeignKey('Product')
    quantity   = models.IntegerField()
    unit_price = models.FloatField()

    class Meta:
        unique_together = ['order', 'product']

以及以下资源,

class ProductResource(ModelResource):
    class Meta:
        resource_name = 'products'
        queryset = Product.objects.all()
        allowed_methods = ['get']

class OrderResource(ModelResource):
    products = fields.ToManyField('agro.api.OrderProductResource', 'orderproduct_set', related_name='product', full=True)

    class Meta:
        resource_name = 'orders'
        queryset = Order.objects.all()
        list_allowed_methods = ['get', 'post']
        detail_allowed_methods = ['get', 'put', 'delete']
        authentication = Authentication() # only for testing purposes
        authorization = Authorization() # only for testing purposes

    class OrderProductResource(ModelResource):
        product = fields.ForeignKey('agro.api.ProductResource', 'product')

        class Meta:
            resource_name = 'orderproducts'
            queryset = OrderProduct.objects.all()
            allowed_methods = ['get']
            include_resource_uri = False
            authentication = Authentication() # only for testing purposes
            authorization = Authorization()  # only for testing purposes

当我尝试使用以下请求数据 POST 到 /orders/ 端点时

{
    "products": [
        {
            "product": "/products/1/",
            "quantity": 4,
            "unit_price": 5
        }
    ]
}

我得到以下错误回溯

{
    "error_message": "orderproduct.order_id may not be NULL",
    "traceback": "Traceback (most recent call last):\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 192, in wrapper\n    response = callback(request, *args, **kwargs)\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 397, in dispatch_list\n    return self.dispatch('list', request, **kwargs)\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 427, in dispatch\n    response = method(request, **kwargs)\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 1165, in post_list\n    updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 1784, in obj_create\n    self.save_m2m(m2m_bundle)\n\n  File \"c:\\Python27\\lib\\site-packages\\tastypie\\resources.py\", line 1951, in save_m2m\n    related_bundle.obj.save()\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\models\\base.py\", line 463, in save\n    self.save_base(using=using, force_insert=force_insert, force_update=force_update)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\models\\base.py\", line 551, in save_base\n    result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\models\\manager.py\", line 203, in _insert\n    return insert_query(self.model, objs, fields, **kwargs)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\models\\query.py\", line 1593, in insert_query\n    return query.get_compiler(using=using).execute_sql(return_id)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\models\\sql\\compiler.py\", line 912, in execute_sql\n    cursor.execute(sql, params)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\backends\\util.py\", line 40, in execute\n    return self.cursor.execute(sql, params)\n\n  File \"c:\\Python27\\lib\\site-packages\\django\\db\\backends\\sqlite3\\base.py\", line 344, in execute\n    return Database.Cursor.execute(self, query, params)\n\nIntegrityError: orderproduct.order_id may not be NULL\n"
}

如您所见,我没有指定新的 OrderProduct 应该关联的 Order,我想要实现的是发布一个 Order 资源,它的嵌套 OrderProduct 数据是在同一个 POST 请求上创建的。

我的问题来了,我如何指定 OrderProduct 引用的订单应该是正在创建的当前订单,最惯用的方法是什么,覆盖水合物(不确定此时我是否有 django orm 订单对象实例因此可以这样做)设置相关模型的顺序或重新实现 hydrate_m2m 甚至 save_m2m,关于如何做到这一点的任何建议?

【问题讨论】:

    标签: django api tastypie


    【解决方案1】:

    感谢您的回答,我确实缺少订单,但在我的情况下,它必须是这样的数据表示要创建的订单以及填充中间表 OrderProduct(json 产品字段)的相关数据,所以这个想法是向 api 的用户传递有关订单的信息以及同一请求中的相关数据,例如创建一个新订单,用户将使用以下数据发布

    {
        "products": {
            "product": "/products/1",
            "quantity": 4,
            "unit_price": 5
        }
    }
    

    这些数据应该生成以下数据库寄存器

    Order
        # with and auto generated id
    
    OrderProduct
        order # which points to the auto generated id
        product # points to a previous registered product
    

    如果我传递了订单字段,我此时没有任何有效的 id(有点像鸡蛋问题),因为订单尚未保存在数据库中,美味派数据流会将订单保存在 ModelResource 上。 obj_save 方法,该方法将调用 ModelResource.save_m2m 来创建发生异常的 OrderProduct 寄存器。

    所以我需要知道在 save_m2m 尝试保存 OrderProduct 之前我必须实现哪个 sweetpie 挂钩才能隐式设置该引用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-18
      • 2012-06-22
      • 2022-01-22
      • 2020-02-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多