我正在为一个项目开发API,我有一个关系 订单/产品 通过订单产品 像这样:
在 models.py
class Product(models.Model):
...
class Order(models.Model):
products = models.ManyToManyField(Product, verbose_name='Products', through='OrderProducts')
...
class OrderProducts(models.Model):
order = models.ForeignKey(Order)
product = models.ForeignKey(Product)
...
现在,当我通过 API 加载订单时,我也想获得相关的产品,所以我尝试了这个(使用 django-tastypie):
按顺序/api.py
class OrderResource(ModelResource):
products = fields.ToManyField('order.api.ProductResource', products, full=True)
class Meta:
queryset = Order.objects.all()
resource_name = 'order'
一切都适用于列出订单资源。我获得嵌入了产品数据的订单资源。
问题是我无法使用 API 创建或编辑订单对象。由于我在 ManytoMany 关系中使用了通过模型,因此 ManyToManyField(products) 没有 .add() 方法。但是 tastypie 在发布/放置数据时尝试在 OrderResource 中的产品字段上调用 .add()。
{"error_message": "'ManyRelatedManager' object has no attribute 'add'", "traceback": "Traceback (most recent call last):nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 192, in wrappern response = callback(request, *args, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 397, in dispatch_listn return self.dispatch('list', request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 427, in dispatchn response = method(request, **kwargs)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1165, in post_listn updated_bundle = self.obj_create(bundle, request=request, **self.remove_api_resource_names(kwargs))nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1784, in obj_createn self.save_m2m(m2m_bundle)nn File "/Library/Python/2.7/site-packages/tastypie/resources.py", line 1954, in save_m2mn related_mngr.add(*related_objs)nnAttributeError: 'ManyRelatedManager' object has no attribute 'add'n"}
由于您只需要 manytomany 字段用于列表,因此更好的解决方案是在 OrderResource
的 products
字段中添加readonly=True
。这消除了重写save_m2m
方法的需要。为了完整起见:
class OrderResource(ModelResource):
products = fields.ToManyField('order.api.ProductResource', products,
readonly=True, full=True)
class Meta:
queryset = Order.objects.all()
resource_name = 'order'
解决方案在于覆盖资源上的 save_m2m() 方法。就我而言,我只需要多对多字段来列出,因此覆盖了 save_m2m() 方法而不执行任何操作。
如果允许您修改class OrderProducts
,添加auto_created = True
可能会解决您的问题,即
class OrderProducts(models.Model):
class Meta:
auto_created = True
如果您无法更改class OrderProducts
,请尝试以下美味派补丁。
---------------------------- tastypie/resources.py ----------------------------
index 2cd869e..aadf874 100644
@@ -2383,7 +2383,20 @@ class BaseModelResource(Resource):
related_resource.save(updated_related_bundle)
related_objs.append(updated_related_bundle.obj)
- related_mngr.add(*related_objs)
+ if hasattr(related_mngr, 'through'):
+ through = getattr(related_mngr, 'through')
+ if not through._meta.auto_created:
+ for related_obj in related_objs:
+ args = dict()
+ args[related_mngr.source_field_name] = bundle.obj
+ args[related_mngr.target_field_name] = related_obj
+ through_obj = through(**args)
+ through_obj.save()
+ else:
+ related_mngr.add(*related_objs)
+ else:
+ related_mngr.add(*related_objs)
def detail_uri_kwargs(self, bundle_or_obj):
"""
在 Django 1.7 中,错误消息更改为"无法在指定中间模型的 ManyToManyField 上设置值"。解决方案是一样的。