19

itertools.groupbyクエリセットの要素をグループ化する際に奇妙な問題が発生しています。私はモデルを持っていますResource:

from django.db import models 

TYPE_CHOICES = ( 
    ('event', 'Event Room'),
    ('meet', 'Meeting Room'),
    # etc 
)   

class Resource(models.Model):
    name = models.CharField(max_length=30)
    type = models.CharField(max_length=5, choices=TYPE_CHOICES)
    # other stuff

私のsqliteデータベースにはいくつかのリソースがあります:

>>> from myapp.models import Resource
>>> r = Resource.objects.all()
>>> len(r)
3
>>> r[0].type
u'event'
>>> r[1].type
u'meet'
>>> r[2].type
u'meet'

したがって、タイプ別にグループ化すると、自然に 2 つのタプルが得られます。

>>> from itertools import groupby
>>> g = groupby(r, lambda resource: resource.type)
>>> for type, resources in g:
...   print type
...   for resource in resources:
...     print '\t%s' % resource
event
    resourcex
meet
    resourcey
    resourcez

今、私は私の見解で同じロジックを持っています:

class DayView(DayArchiveView):
    def get_context_data(self, *args, **kwargs):
        context = super(DayView, self).get_context_data(*args, **kwargs)
        types = dict(TYPE_CHOICES)
        context['resource_list'] = groupby(Resource.objects.all(), lambda r: types[r.type])
        return context

しかし、テンプレートでこれを繰り返すと、いくつかのリソースが欠落しています。

<select multiple="multiple" name="resources">
{% for type, resources in resource_list %}
    <option disabled="disabled">{{ type }}</option>
    {% for resource in resources %}
        <option value="{{ resource.id }}">{{ resource.name }}</option>
    {% endfor %}
{% endfor %}
</select>

これは次のようにレンダリングされます。

複数選択

サブイテレータがすでに反復されていると考えていますが、これがどのように発生するかはわかりません。

(python 2.7.1、Django 1.3 を使用)。

(編集: 誰かがこれを読んだら、 を使用する代わりに組み込みのregroupテンプレート タグgroupbyを使用することをお勧めします。)

4

2 に答える 2

20

私はあなたが正しいと思います。理由はわかりませんが、groupbyイテレータが事前に反復されているように見えます。コードで説明する方が簡単です:

>>> even_odd_key = lambda x: x % 2
>>> evens_odds = sorted(range(10), key=even_odd_key)
>>> evens_odds_grouped = itertools.groupby(evens_odds, key=even_odd_key)
>>> [(k, list(g)) for k, g in evens_odds_grouped]
[(0, [0, 2, 4, 6, 8]), (1, [1, 3, 5, 7, 9])]

ここまでは順調ですね。しかし、イテレータの内容をリストに格納しようとするとどうなるでしょうか?

>>> evens_odds_grouped = itertools.groupby(evens_odds, key=even_odd_key)
>>> groups = [(k, g) for k, g in evens_odds_grouped]
>>> groups
[(0, <itertools._grouper object at 0x1004d7110>), (1, <itertools._grouper object at 0x1004ccbd0>)]

確かに、結果をキャッシュしたばかりで、イテレータはまだ良好です。右?違う。

>>> [(k, list(g)) for k, g in groups]
[(0, []), (1, [9])]

キーを取得するプロセスでは、グループも繰り返し処理されます。つまり、キーをキャッシュしてグループを破棄し、最後のアイテムを保存します。

django がイテレータをどのように処理するかはわかりませんが、これに基づいて、イテレータをリストとして内部的にキャッシュしていると思います。上記を実行することで、この直感を少なくとも部分的に確認できますが、より多くのリソースが必要です。表示されている唯一のリソースが最後のリソースである場合、上記の問題がどこかにあることはほぼ確実です。

于 2011-08-02T02:57:22.997 に答える