【问题标题】:How to make POST get_or_create related Resources in tastypie如何在tastepie中POST get_or_create相关资源
【发布时间】:2014-09-04 13:31:22
【问题描述】:

我正在使用 Tastypie 为 Django 应用程序创建 REST API,并希望能够在一个 POST 中创建新对象和相关对象。相关对象由用于查找它们的名称指定,如果找不到名称,我想创建新对象。

给定这样的 Django 模型:

class Product(Model):
    name = CharField(max_length=32)

class Release(Model):
    product = ForeignKey(to=Product, related_name='releases')
    version = CharField(max_length=32)

还有这些 Tastypie 资源:

class ProductResource(ModelResource):
    class Meta:
        queryset = Product.objects.all()
        resource_name = 'product'

class ReleaseResource(ModelResource):
    class Meta:
        queryset = Release.objects.all()
        resource_name = 'release'

    def hydrate_product(self, bundle):
        """Replace product name with id of existing/created one."""
        product = Product.objects.get_or_create(name=bundle.data['product'])
        bundle.data['product'] = product.id
        return bundle

还有一个空数据库

当我将此数据发布到我的 sweetpie REST API 时:

POST /api/release {
    "product": "Cool Widget",
    "version": "1.2.3",
}

然后我想创建这些模型对象:

product = Product(name="Cool Widget")
release = Release(product=product, version="1.2.3")

但我得到一个类似这样的异常:

IntegrityError: null value in column "product_id" violates not-null constraint
DETAIL:  Failing row contains (1, null, 1.2.3).

并且没有调用 hydrate_product() 方法。

当我将这个类属性添加到 ReleaseResource 时:

product = fields.ToOneField(ProductResource, 'product')

然后我得到这样的东西:

NotFound: An incorrect URL was provided 'Cool Widget' for the 'ProductResource' resource.

如何将捆绑包中的产品名称替换为具有该名称的已创建/现有产品对象的 URI?

【问题讨论】:

    标签: tastypie


    【解决方案1】:

    我认为我的错误在于试图修改关系字段的用途。如果我的资源实际上包含它们,它们应该可以正常工作。

    在创建新的Release 时,我应该将ReleaseResource.product 声明为CharField 并实现Release.obj_create 以将Product.objects.get_or_create 用于相关的Product 对象。

    class ReleaseResource(ModelResource):
        product = CharField(attribute='product__name')
    
        class Meta:
            queryset = Release.objects.all()
            resource_name = 'release'
    
        def obj_create(self, bundle, **kwargs)
            product = Product.objects.get_or_create(name=bundle.data['product'])[0]
            super(ModelResource, self).obj_create(bundle, product=product, **kwargs)
    

    在文档中似乎不够清楚 no 关系字段自动填充到 ModelResource 上,您必须明确声明 all 关系字段,而不是倒过来就好了。

    【讨论】:

      【解决方案2】:

      这里:

      class ReleaseResource(ModelResource):
          class Meta:
              queryset = Release.objects.all()
              resource_name = 'release'
      
          def hydrate_product(self, bundle):
              """Replace product name with id of existing/created one."""
              product = Product.objects.get_or_create(name=bundle.data['product'])
              bundle.data['product'] = product.id
              return bundle
      

      hydrate_product 不会被触发,因为 hydrate_* 是基于 Tastypie 字段生成的,默认情况下相关的字段不存在。

      我不是 100% 确定,但我会试试这个:

      class ReleaseResource(ModelResource):
          class Meta:
              queryset = Release.objects.all()
              resource_name = 'release'
      
          def hydrate(self, bundle):
              """Replace product name with id of existing/created one."""
              product = Product.objects.get_or_create(name=bundle.data['product'])
              bundle.obj.product = product
              return bundle
      

      注意:我将产品对象分配给bundle.obj.product

      【讨论】:

      • 谢谢,最后我发现最简单的解决方案是为名称声明一个 CharField 并在 obj_create 的实现中 get_or_create 相关对象。请参阅我在此线程上的答案。
      猜你喜欢
      • 1970-01-01
      • 2019-09-12
      • 2012-11-02
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 2013-06-07
      • 1970-01-01
      • 2016-07-14
      相关资源
      最近更新 更多