2

カスタム ModelManager に配置した複雑なビジネス ロジックがあります。manager メソッドは、クエリセットではなく値のタプルを返します。これは悪い習慣と見なされますか? もしそうなら、推奨されるアプローチは何ですか。ビューにロジックを入れたくないのですが、Django にはサービス層がありません。さらに、私のロジックは潜在的に複数のクエリを実行する必要があります。

ロジックは、現在の時間に最も近いイベントを選択する必要があり、加えて両側に 3 つのイベントがあります。テンプレートに配置する場合、最も近いイベントを知っておくと役立ちます。これは、全画面スライダーに最初に表示されるイベントだからです。

現在の呼び出しは次のとおりです。

closest_event, previous_events, next_events = Event.objects.closest()

ロジックは現在正常に動作します。アプリを変換しようとしています。テンプレートでイベント データを JSON としてレンダリングし、ページの読み込み時に backbone.js ビューをブートストラップできるようにします。TastyPie を使用して、サーバー側のリソースをテンプレートにレンダリングする予定です。コードをリファクタリングする前に、現在のアプローチが悪い習慣とは見なされていないことを知っておくとよいでしょう。

これが私のアプリです。現在動作しています:

ビュー.py

class ClosestEventsListView(TemplateView):
    template_name = 'events/event_list.html'

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        closest_event, previous_events, next_events = Event.objects.closest()

        context['closest_event'] = closest_event
        context['previous_events'] = previous_events
        context['next_events'] = next_events
        return self.render_to_response(context)

models.py

from datetime import timedelta
from django.db import models
from django.utils import timezone

from model_utils.models import TimeStampedModel

class ClosestEventsManager(models.Manager):
    def closest(self, **kwargs):
        """
        We are looking for the closest event to now plus the 3 events either side.
        First select by date range until we have a count of 7 or greater
        Initial range is 1 day eithee side, then widening by another day, if required
        Then compare delta for each event data and determine the closest
        Return closest event plus events either side
        """
        now = timezone.now()
        range_in_days = 1
        size = 0

        while size < 7:
            start_time = now + timedelta(days=-range_in_days)
            end_time = now + timedelta(days=range_in_days)
            events = self.filter(date__gte=start_time, date__lte=end_time, **kwargs).select_related()
            size = events.count()
            range_in_days += 1

        previous_delta = None
        closest_event = None
        previous_events = None
        next_events = None
        position = 0

        for event in events:
            delta = (event.date - now).total_seconds()
            delta = delta * -1 if delta < 0 else delta
            if previous_delta and previous_delta <= delta:
                # we have found the closest event. Now, based on
                # position get events either size
                next_events = events[:position-1]
                previous_events = events[position:]
                break

            previous_delta = delta
            closest_event = event
            position += 1

        return closest_event, previous_events, next_events


class Event(TimeStampedModel):

    class Meta:
        ordering = ['-date']

    topic = models.ForeignKey(Topic)
    event_type = models.ForeignKey(EventType)
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    date = models.DateTimeField(db_index=True)
    end_time = models.TimeField()
    location = models.ForeignKey(Location)
    twitter_hashtag = models.CharField(null=True, blank=True, max_length=100)
    web_link = models.URLField(null=True, blank=True)

    objects = ClosestEventsManager()

    def __unicode__(self):
        return self.title
4

2 に答える 2

3

タプルを返すのは悪い習慣ではないと思います。ModelManagerドキュメントの最初の例はリストを返します。

そうは言っても、代わりにクエリセットを作成したい場合は、次のようなことを行うことができます-

def closest(self, **kwargs):
    # get the events you want
    return self.filter(pk__in=([event.id for event in events]))
于 2013-03-06T12:02:06.137 に答える
2

それは大丈夫です、Django自身でさえget_or_createそれをします。関数を使用している人には、連鎖できない(つまり、クエリセットを返さない)ことが明確になっていることを確認してください。

于 2013-03-06T21:30:10.803 に答える