2013-07-29 25 views
1

lookups that span relationships类似,我有一个对象,我想动态评估指定相关对象属性的字符串。django评估跨越对象上的关系的字符串

例如对一个question_response对象我想要评估survey_response__responder__first_name

在列表中,我指定了要查找对象并导出到csv的属性。例如['title','question_response_id']。所以基本上我的脚本得到一个对象列表,然后抓取所有指定的属性并将数据放入csv中。 (其实它是我正在使用的django tablib)。

我希望能够指定该对象上的属性,但是关系上的属性。我已经有了这个对象,所以我不是从一个对象管理器开始的。我试图弄清楚是否可以使用跨越关系并评估它的属性字符串。

回答

1

Django管理应用程序能已经做到这一点,所以我挖周围的源和django.contrib.admin.utils.py有很多,在查询解析查询字符串,其字段,以连锁它们作为过滤器部分实用功能组。特别感兴趣的是get_field_from_path

def get_fields_from_path(model, path): 
    """ Return list of Fields given path relative to model. 

    e.g. (ModelX, "user__groups__name") -> [ 
     <django.db.models.fields.related.ForeignKey object at 0x...>, 
     <django.db.models.fields.related.ManyToManyField object at 0x...>, 
     <django.db.models.fields.CharField object at 0x...>, 
    ] 
    """ 
    pieces = path.split(LOOKUP_SEP) 
    fields = [] 
    for piece in pieces: 
     if fields: 
      parent = get_model_from_relation(fields[-1]) 
     else: 
      parent = model 
     fields.append(parent._meta.get_field_by_name(piece)[0]) 
    return fields 

与其它功能从utils.py结合这一点,你有你的解决方案。

+0

啊有趣。很酷。必须将其切换到可接受的解决方案。 –

2

据我所知,不是直接。但是,如果你愿意做另一个数据库命中,就可以轻松完成:

fields = [ 
    'survey_response__responder__first_name', 
    'survey_response__responder__last_name', 
] 
known_objects = [obj1, obj2, obj3] 
pks = [obj.pk for obj in known_objects] 

SomeModel.objects.filter(pk__in = pks).values_list(*fields) 

除此之外,做一个查询所有这些数据可能是正确的路要走; obj1.survey_response.responder.first_name将要做2个查询:一个用于响应,然后是另一个用于响应者,以及更多,如果你正在循环使用obj2obj3等,如果你还没有select_related()就可以了。

+0

待机。澄清上面。 –

+0

@BobSpryn Ping for update – Izkata

+0

这是一个只发生一次和一次的管理任务,所以前面的解决方案可能会很好。我宁愿不要太多地破解管理员tablib,但不想为每个相关字段指定函数而不是字符串。 –

1

仅供参考我发现了另一种类似于@ Burhan建议的解决方案,但没有利用utils.py函数。

实测值有关此片段:http://djangosnippets.org/snippets/2868/

即所述prep_field方法:

def prep_field(obj, field): 
    """ Returns the field as a unicode string. If the field is a callable, it 
    attempts to call it first, without arguments. 
    """ 
    if '__' in field: 
     bits = field.split('__') 
     field = bits.pop() 

     for bit in bits: 
      obj = getattr(obj, bit, None) 

      if obj is None: 
       return "" 

    attr = getattr(obj, field) 
    output = attr() if callable(attr) else attr 
    return unicode(output).encode('utf-8') if output else "" 
+0

顺便说一下,这似乎是['lookup_field']的副本(https://github.com/django/django/blob/master/django/contrib/admin/util.py#L244) –

+1

我知道这个对于你的用例无关紧要,但是我会注意到其他人可能没有意识到:在循环中每次调用getattr都会加载另一个模型,因此每次迭代都意味着数据库命中,除非它们之前已加载。 ([更多信息和示例](https://docs.djangoproject.com/en/1。4/ref/models/querysets /#django.db.models.query.QuerySet.select_related)) – Izkata

+0

是的。你会希望这是一个芹菜任务,或者如果你正在做任何深入的远程任务。 –