使用Tastypie和Django通过一个POST请求创建多个对象的正确方法

时间:2014-01-16 11:09:53

标签: django rest tastypie

假设我有两个Django模型,ModelA和ModelB。在某些情况下,ModelA对象的创建应该触发ModelB对象的创建。目前,在没有使用Tastypie的情况下,我有以下视图来控制A对象的创建:

def new_A_created_view(request):
    post_data = json.loads(request.body)
    ... parse post arguments ...

    a = ModelA.objects.create(...)
    if conditions_are_met(...):
       b = ModelB.objects.create(...)

现在我想转换到REST Api,并使用TastyPie我有以下资源:

class ModelAResource(ModelResource):

    class Meta:
        queryset = ModelA.objects.all()
        authorization = Authorization()
        ...

    def hydrate(self, bundle):
        if bundle.request.META['REQUEST_METHOD'] == 'POST':
           ...compute objects attributes from received post data ...

我知道以下选项来创建相应的B对象:

  1. 覆盖save或post_save方法。覆盖保存方法或在ModelA中添加post_save信号,以便创建对象B后立即执行保存A.我在此方法中看到的问题:

    • 创建对象ModelB的逻辑可能不依赖于A中存在的属性,而是取决于POST请求中包含的参数。
    • 我在views.py中失去了漂亮的'控制器'逻辑,转而支持模型中更模糊的逻辑。
  2. 覆盖ModelAResource中的Resource.obj_create ,以便根据资源POST信息创建ModelA对象和ModelB对象(如果需要)。在这种情况下,我也觉得对象的创建在某种程度上被遮挡了。

  3. 我遗失的任何解决方案?最好的方法是什么?

2 个答案:

答案 0 :(得分:0)

因为ModalA与ModelB有关系,并且您希望在保存ModelA时自动保存ModelB,所以您的资源声明应该是,

from tastypie.resources import ModelResource
from tastypie import fields
from yourapp.models import ModelA, ModelB

class ModelAResource(ModelResource):
    model_b = fields.ForeignKey(
        'yourapp.resources.ModelBResource',
        'model_b',
        full=True
    )
    # ....
    class Meta:
        queryset = ModelA.objects.all()
        # ....

class ModelBResource(ModelResource):
    # ....
    class Meta:
        queryset = ModelB.objects.all()
        # ....

当您发布/ PUT / PATCH时,您只需将model_b放入数据中,就像创建/编辑model_b

一样

例如

{
    "field1_of_model_a" : "content_of_field1_of_model_a",
    "field2_of_model_a" : "content_of_field2_of_model_a",
    "model_b" : {
        "field_1_of_model_b" : "content_of_field_1_of_model_b",
        "field_2_of_model_b" : "content_of_field_2_of_model_b"
    }
}

答案 1 :(得分:0)

最后,我在资源类中添加了一个url,覆盖了资源的入口点,并将其定向到执行所需逻辑的类的自定义函数。我也从资源中删除了任何保湿剂。

class ModelAResource(ModelResource):

    class Meta:
        queryset = ModelAResource.objects.all()
        authentication = MyOwnAuthentication()
        resource_name = 'modelAResource'
        allowed_methods = ['post']

    def prepend_urls(self):
        return [url(r"^(?P<resource_name>%s)%s$" %
                (self._meta.resource_name, trailing_slash()),
                self.wrap_view('process_resource'), name="process_resource")]

    def process_resource(self, request, **kwargs):
        self.method_check(request, allowed=['post'])
        self.is_authenticated(request)

        data = self.deserialize(request, request.body)

        param1 = data.get('param1name', '')
        param2 = data.get('param2name', '')

        ModelA.objects.create(...)
        if it_should_be_done(param1, param2):
            ModelB.objects.create(...)

当然这有一些注意事项,因为我已经覆盖了访问API的默认网址,但在我的情况下这没有问题,因为API只会在资源上公开一个操作(创建)。无论如何,前缀的网址可以是任何东西。