【问题标题】:Django tastypie and GenericForeignKeyDjango sweetpie 和 GenericForeignKey
【发布时间】:2012-01-01 12:06:27
【问题描述】:

我有 GFK 的页面模型。

class Page(models.Model):
    title = models.CharField(max_length=200)
    content_type = models.ForeignKey(ContentType,null=True,blank=True)
    object_id = models.CharField(max_length=255,null=True,blank=True)
    content_object = generic.GenericForeignKey('content_type', 'object_id')

class TextContent(models.Model):
    content = models.TextField(null=True, blank=True)
    pages = generic.GenericRelation(Page)

我做了 Page.objects.get(pk=1).content_object,我得到了。

请帮我在 REST 中显示一个锚定对象的链接(或输出到 JSON)。

class PageResource(ModelResource):
    content_object = fields.?????

    class Meta:
        queryset = Page.objects.all()
        resource_name = 'page'

怎么做才对?

谢谢!

活力

【问题讨论】:

  • 你有没有在不将补丁应用到美味派的情况下完成这项工作?

标签: django rest django-models tastypie


【解决方案1】:

目前在 sweetpie 中使用泛型关系没有简单的方法。在tastypie github page 提交了一些补丁,但在撰写本文时尚未合并。

最简单的方法是定义一个内容类型资源并将其用于具有通用关系的资源。大致如下:

class ContentTypeResource(ModelResource):
    class Meta:
        queryset = ContentType.objects.all()
        resource_name = "contrib/contenttype"
        fields = ['model']
        detail_allowed_methods = ['get',]
        list_allowed_methods = ['get']

class PageResource(ModelResource):
    content_object = fields.ToOneField('myresources.ContentTypeResource', 'content_object')


    class Meta:
        queryset = Page.objects.all()
        resource_name = 'page'

希望这会有所帮助。

【讨论】:

  • 我现在正在尝试实现这一点。您能否提供更多背景信息以帮助我理解。我有一个 Additive,它与 Attributes 有一般关系。我想获得给定添加剂的所有属性。在您的示例中,“myresources”、“contrib/contenttype”、“model”指的是什么。谢谢。
  • 请注意,在实现这一点时困扰我的一些事情:如果您使用反向泛型关系,您不能忘记在访问泛型资源的模型上使用 fields.ToManyField。跨度>
【解决方案2】:

“myresources”是包含 ContentTypeResource 的应用程序。如果它与您的其他资源位于同一应用程序中,则无需对其进行限定。在下面的代码中删除。

“contrib/contenttype”是资源的名称。设置您自己的名称是可选的。如果您不指定,Tastypie 会为您创建一个。我已经在下面的更新代码中删除了。

fields = ['model'] 部分限制了此资源所代表的模型的可访问字段。如果您查看 Django 代码中 ContentType 模型的定义,您会看到它有一个名为“模型”的字段。

我认为原始答案的字段名称混淆了。您正在尝试为 content_type 创建一个新资源,并将其连接到模型中的 content_type 外键。上面的代码解决了这个问题。

class ContentTypeResource(ModelResource):
    class Meta:
        queryset = ContentType.objects.all()
        fields = ['model']
        detail_allowed_methods = ['get',]
        list_allowed_methods = ['get']

class PageResource(ModelResource):
    content_type = fields.ToOneField('ContentTypeResource', 'content_type')

    class Meta:
        queryset = Page.objects.all()
        resource_name = 'page'

您还需要在您的 urls.py 中注册 ContentTypeResource,就像您使用所有其他资源一样:

from myapp.api import ContentTypeResource

v1_api = Api(api_name='v1')
v1_api.register(ContentTypeResource())

“myapp”位同样是 API 代码包含 ContentTypeResource 的应用程序。

我希望这可以解决问题。我自己就搞定了……

【讨论】:

    【解决方案3】:

    看起来这是一个月前正式添加到 Tastypie 的,请在此处查看示例。

    https://github.com/toastdriven/django-tastypie/blob/master/docs/content_types.rst

    【讨论】:

    • 但这需要知道您将指向哪些模型。哪种方式首先违背了拥有 GenericForeignKey 的目的。
    【解决方案4】:

    我们破解了密码!

    class ContentTypeResource(ModelResource):
    
        class Meta:
            queryset = ContentType.objects.all()
            resource_name = 'content_type'
            allowed_methods = ['get',]
    
    class PageObjectResource(ModelResource):
    
        content_object = fields.CharField()
    
        content_type = fields.ToOneField(
            ContentTypeResource,
            attribute = 'content_type',
            full=True)
    
        class Meta:
            queryset = models.PageObject.objects.all()
            resource_name = 'page_object'
            allowed_methods = ['get',]
    
        def dehydrate_content_object(self, bundle):
            for resource in api._registry.values():
                if resource._meta.object_class == bundle.obj.content_object.__class__:
                    return resource.full_dehydrate(resource.build_bundle(obj=bundle.obj.content_object, request=bundle.request)).data
            return ''
    

    结果如下:

    "page_objects": [
    {
    "content_object": {
    "id": "186",
    "look_stills": [
    {
    "_image": "/static/media/uploads/looks/DSC_0903_PR_MEDIUM_QUALITY_RGB_FA.jpg",
    "aspect": "front",
    "id": "186",
    "look_still_icons": [
    {
    "colour_code": "58",
    "enabled": true,
    "id": "186",
    "in_stock_only": true,
    "look_product": {
    "colour_code": "58",
    "enabled": true,
    "id": "186",
    "resource_uri": "/api/look_product/186/",
    "style_code": "420215"
    },
    "resource_uri": "/api/look_still_icon/186/",
    "x_coord": 76,
    "y_coord": 5
    }
    ],
    "ordering": 1,
    "resource_uri": "/api/look_still/186/"
    }
    ],
    "resource_uri": "/api/look_still_set/186/",
    "slug": ""
    },
    "content_type": {
    "app_label": "looks_beta",
    "id": "97",
    "model": "lookstillset",
    "name": "look still set",
    "resource_uri": "/api/content_type/97/"
    },
    "id": "2",
    "object_id": 186,
    "resource_uri": "/api/page_object/2/"
    }
    ],
    "page_order": 3,
    "page_template": "look_still",
    "resource_uri": "/api/page/2/",
    "slug": "",
    "spread_number": 2,
    "title": ""
    },
    

    【讨论】:

      【解决方案5】:

      这为您提供了作为嵌套对象的 content_object 字段。它很简单,很有效,而且(不幸的是)它的效率与技术所允许的一样高。

      class PageResource(ModelResource):
      
          def full_dehydrate(self, bundle):
              new_bundle = super(PageResource, self).full_dehydrate(bundle)
              new_bundle.data['content_object'] = get_serializable(bundle.obj.content_object)
              return new_bundle
      
          class Meta:
              queryset = Page.objects.all()
      
      
      def get_serializable(model):
      
          data = {'type': model.__class__.__name__}
          for field in model._meta.fields:
              data[field.name] = getattr(model, field.name)
          return data
      

      【讨论】:

        【解决方案6】:

        我们设法获取内容对象的 uri,如果它有相应的 ModelResource:

        class ContentTypeResource(ModelResource):
        
            class Meta:
                queryset = ContentType.objects.all()
                resource_name = 'content_type'
                allowed_methods = ['get',]
        
        class PageObjectResource(ModelResource):
        
            content_object_uri = fields.CharField()
        
            content_type = fields.ToOneField(
                ContentTypeResource,
                attribute = 'content_type',
                full=True)
        
            class Meta:
                queryset = models.PageObject.objects.all()
                resource_name = 'page_object'
                allowed_methods = ['get',]
        
            def dehydrate_content_object_uri(self, bundle):
                for resource in api._registry.values():
                    if resource._meta.object_class == bundle.obj.content_object.__class__:
                        return resource.get_resource_uri(bundle.obj.content_object)
                return ''
        

        【讨论】:

          【解决方案7】:

          事实上,正如马里奥建议的那样,他们已经增加了对此的支持。由于花了很长时间才弄清楚我认为这可能会帮助一些人。这是一个使用 Django 内置注释模型的示例,我从注释对象中获得与 cmets 的反向关系:

          将此添加到 cmets 附加到的模型中:

          class CmntedObject(models.Model):
              comments = generic.GenericRelation(Comment,
                                     content_type_field='content_type',
                                     object_id_field='object_pk')
          

          资源看起来像这样:

          class UserResource(ModelResource):
              what ever you need here....
          
          class CmntedObjectResource(ModelResource):
              comments = fields.ToManyField('path.to.api.CmntedObjectResource', 'comments', full=True, null=True)
              class Meta:
                  queryset = CmntedObject.objects.all()
                  resource_name = 'cmntedobject'
                  allowed_methods = ['get', 'post', 'delete']
                  authorization = DjangoAuthorization()
          
          class CommentResource(ModelResource):
              user = fields.ToOneField('path.to.api.UserResource', 'user', full=True)
              content_type_id = fields.CharField(attribute = 'content_type_id')
              site_id = fields.CharField(attribute = 'site_id')
              content_object = GenericForeignKeyField({
                                 CmntedObject: CmntedObjectResource, #shown above
                                 OtherCmntedObject: OtherCmntedObjectResource, #optional
                              }, 'content_object', null=True)
          
              class Meta:
                  queryset = Comment.objects.all()
                  resource_name = 'cmnt'
                  allowed_methods = ['get', 'post', 'delete']
                  authorization = DjangoAuthorization()
          
              def obj_create(self, bundle, **kwargs):
                  #here we get the current authenticated user as the comment user.
                  bundle = super(CmntResource, self).obj_create(bundle, user=bundle.request.user)
                  return bundle
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-01-01
            • 2012-12-04
            • 1970-01-01
            • 1970-01-01
            • 2011-10-24
            • 2014-04-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多