【问题标题】:Proper way to create more than one object with one POST request using Tastypie and Django使用 Tastypie 和 Django 通过一个 POST 请求创建多个对象的正确方法
【发布时间】:2014-01-16 11:09:53
【问题描述】:

假设我有两个 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 方法。重写 save 方法或在 ModelA 中添加一个 post_save 信号,以便对象 B 的创建立即遵循保存 A 的行为。我在这种方法中看到的问题:

    • 创建对象 ModelB 的逻辑可能不取决于 A 中存在的属性,而是取决于 POST 请求中包含的参数。
    • 我在 views.py 中失去了很好的“控制器”逻辑,取而代之的是模型内部更模糊的逻辑。
  2. 在 ModelAResource 中覆盖 Resource.obj_create,以便它根据资源 POST 信息创建一个 ModelA 对象和一个 ModelB 对象(如果需要)。在这种情况下,我也觉得对象的创建在某种程度上被掩盖了。

我缺少任何解决方案吗?最好的方法是什么?

【问题讨论】:

  • 你能告诉我们ModelA和ModelB是如何在models.py中声明的吗?他们俩有关系吗?
  • 在我的特殊情况下,ModelA 将 ModelB 作为其字段之一。所以在 ModelA 内部会有一个定义为 object_b = models.ForeignKey(ModelB) 的字段。 ModelB 与 ModelA 没有直接关系。

标签: django rest tastypie


【解决方案1】:

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

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()
        # ....

当您 POST/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"
    }
}

【讨论】:

  • 感谢@adityasdarma1,虽然这不是我想要做的,但我没有考虑过这些选项。 ModelB 应该在服务器中自动创建,而不是通过 POST 接收。为了澄清我所追求的,ModelA 只是一个 url,而 ModelB 是该 url 的域加上一些统计信息。因此,当对 ResourceA 进行一个 POST 时,我会检查该 url 的域对象是否存在于数据库中,如果不存在则创建它。我想让这个问题更笼统,但这是我的特殊情况:)。
【解决方案2】:

最后,我在资源类中添加了一个 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(...)

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

【讨论】:

    猜你喜欢
    • 2021-08-01
    • 2019-03-12
    • 2017-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-23
    • 1970-01-01
    • 2014-05-11
    相关资源
    最近更新 更多