在tastypie视图中公开“虚拟”字段?

时间:2011-06-11 18:30:04

标签: ajax django tastypie

我想使用tastypie创建一个视图来暴露相同类型的某些对象,但是使用以下两个三个曲折:

  1. 我需要使用三个单独的查询来获取对象;
  2. 我需要添加一个在底层模型中不存在的字段,该字段的值取决于它来自哪个查询;和
  3. 数据将是每个用户(所以我需要挂钩到获取请求的方法之一)。
  4. 我不清楚如何进入tastypie生命周期来实现这一目标。添加“虚拟”字段的推荐方法是在脱水方法中,该方法只知道它正在操作的包。

    更糟糕的是,没有正式的方法来加入查询集。

    如果我能让tastypie接受查询集以外的东西,我的问题就会消失。在这种情况下,我可以传递一个我的对象的子类列表,并添加了附加字段。

    我愿意接受任何其他合理的解决方案。

    编辑:添加了扭曲3 - 每用户数据。

4 个答案:

答案 0 :(得分:8)

在最后一个版本中,您应该覆盖脱水方法,例如

def dehydrate(self, bundle):
    bundle.data['full_name'] = bundle.obj.get_full_name()
    return bundle

答案 1 :(得分:7)

在此遇到类似问题。在我的情况下,列表中的项目可以由用户“检查”。

  • 当AJAX检索项目时,将返回其检查状态,并将资源作为普通字段。
  • 当项目保存到服务器时,资源中的“已检查”字段存储在用户的会话中。

首先,我认为hydrate()dehydrate()方法是这项工作的最佳匹配,但事实证明在这些方法中访问request对象存在问题。所以我选择了alter_data_to_serialize()obj_update()。我认为没有必要覆盖obj_create(),因为我认为,首次创建项目时无法检查项目。

以下是代码,但请注意,它尚未经过正确测试。

class ItemResource(ModelResource):
    def get_object_checked_status(self, obj, request):
        if hasattr(request, 'session'):
            session = request.session
            session_data = session.get(get_item_session_key(obj), dict())
            return session_data.get('checked', False)
        return False

    def save_object_checked_status(self, obj, data, request):
        if hasattr(request, 'session'):
            session_key = get_item_session_key(obj)
            session_data = request.session.get(session_key, dict())
            session_data['checked'] = data.pop('checked', False)
            request.session[session_key] = session_data

    # Overridden methods
    def alter_detail_data_to_serialize(self, request, bundle):
        # object > resource
        bundle.data['checked'] = \
            self.get_object_checked_status(bundle.obj, request)
        return bundle

    def alter_list_data_to_serialize(self, request, to_be_serialized):
        # objects > resource
        for bundle in to_be_serialized['objects']:
            bundle.data['checked'] = \
                self.get_object_checked_status(bundle.obj, request)
        return to_be_serialized

    def obj_update(self, bundle, request=None, **kwargs):
        # resource > object
        save_object_checked_status(bundle.obj, bundle.data, request)
        return super(ItemResource, self)\
            .obj_update(bundle, request, **kwargs)

def get_item_session_key(obj): return 'item-%s' % obj.id

答案 2 :(得分:1)

好的,这是我的解决方案。代码如下。

注意事项:

  1. 基本上所有工作都在obj_get_list完成。这就是我运行查询的地方,可以访问请求。
  2. 我可以从obj_get_list返回一个列表。
  3. 如果我希望它们可用,我可能必须覆盖与其他操作相对应的所有其他obj_*方法(如obj_getobj_create等)。
  4. 因为我queryset中没有Meta,我需要提供一个object_class来告诉tastypie内省提供哪些字段。
  5. 要公开我的“虚拟”属性(我在obj_get_list中创建),我需要为其添加一个字段声明。
  6. 我已经注释掉了过滤器和授权限制,因为我现在不需要它们。如果我需要,我需要自己实施。
  7. 代码:

    from tastypie.resources import ModelResource
    from tastypie import fields
    from models import *
    import logging
    
    logger = logging.getLogger(__name__)
    
    
    class CompanyResource(ModelResource):
        role = fields.CharField(attribute='role')
    
    
        class Meta:
            allowed_methods = ['get']
            resource_name = 'companies'
            object_class = CompanyUK
            # should probably have some sort of authentication here quite soon
    
    
        #filters does nothing. If it matters, hook them up
        def obj_get_list(self, request=None, **kwargs):
    #         filters = {}
    
    #         if hasattr(request, 'GET'):
    #             # Grab a mutable copy.
    #             filters = request.GET.copy()
    
    #         # Update with the provided kwargs.
    #         filters.update(kwargs)
    #         applicable_filters = self.build_filters(filters=filters)
    
            try:
                #base_object_list = self.get_object_list(request).filter(**applicable_filters)
                def add_role(role):
                    def add_role_company(link):
                        company = link.company
                        company.role = role
                        return company
                    return add_role_company
    
                director_of = map(add_role('director'), DirectorsIndividual.objects.filter(individual__user=request.user))
                member_of   = map(add_role('member'),   MembersIndividual.objects.filter(individual__user=request.user))
                manager_of  = map(add_role('manager'),  CompanyManager.objects.filter(user=request.user))
    
                base_object_list = director_of + member_of + manager_of
                return base_object_list #self.apply_authorization_limits(request, base_object_list)
            except ValueError, e:
                raise BadRequest("Invalid resource lookup data provided (mismatched type).")
    

答案 3 :(得分:0)

你可以这样做(未经测试):

def alter_list_data_to_serialize(self, request, data):

    for index, row in enumerate(data['objects']):

        foo = Foo.objects.filter(baz=row.data['foo']).values()
        bar = Bar.objects.all().values()

        data['objects'][index].data['virtual_field'] = bar

    return data