2013-07-29 50 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

是的。你會希望這是一個芹菜任務,或者如果你正在做任何深入的遠程任務。 –