3

私はこのようなモデルを持っています(簡略化):

class TrainingMoment(models.Model):
    date = models.DateField()
    # Moment of the day, 2 is afternoon for example
    moment_nr = models.IntegerField()
    is_group_moment = models.BooleanField()

特定の日付と moment_nr には、2 つの行が存在する可能性があります。1is_group_moment=Falseつと 1 つis_group_moment=True。の重複のすべてについてTrainingMoment.objects.filter(date__range=(start_date,end_date))、 で行を除外したいis_group_moment = True

is_group_moment=Trueで行を除外したいことに注意してくださいis_group_moment=False

最初に重複行を取得するために annotate() を group_by() と一緒に使用しようとしましたが、必要に応じて両方ではなく、各重複行セットの 1 つだけが med に与えられます。

例えば:

╔════════════════════════════════════════╗
║ date       moment_nr is_group_moment   ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True              ║
║ 2013-10-02 1         False             ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True              ║
║ 2013-10-04 2         False             ║
║ 2013-10-01 1         True              ║
╚════════════════════════════════════════╝

次のようにする必要があります。

╔═════════════════════════════════════════╗
║ date       moment_nr is_group_moment    ║
╠═════════════════════════════════════════╣
║ 2013-10-01 1          True              ║
║ 2013-10-02 1          False             ║
║ 2013-10-03 1          False             ║
║ 2013-10-03 2          False             ║
║ 2013-10-04 2          False             ║
║ 2013-10-01 1          True              ║
╚═════════════════════════════════════════╝

上記のクエリセットを使用して週の正しい合計を取得するトレーニング時間を格納する別のモデル Activity があります。

class Activity(models.Model):
    activitytype = models.ForeignKey(ActivityType)
    trainingmoment = models.ForeignKey(TrainingMoment)
    # Time in minutes
    time = models.IntegerField()

私が達成したいことの要約:

# 1. Get training moments for a given period, for example a week (no problem here)
tms_for_summing = TrainingMoment.objects.filter(date__range=(start_date,end_date))

# 2. Filter out duplicates in the way described above

# 3. Use the resulting queryset (tms_for_summing) to sum activity
summed_activity = Activity.objects.filter(trainingmoment__in = tms_for_summing)    

編集 ここでは、私のデータベース設計について疑問に思っている人のために、いくつかの追加説明を行います。

これは、おそらくご存知のとおり、トレーニング ログ アプリです。私の質問の場合、実現したいページは企画ページです。すべてのトレーニングの瞬間は、計画されたトレーニングの瞬間です。個々のアスリートは、自分のトレーニングを計画できます。また、トレーナーはアスリートのグループのトレーニングを同時に計画できます。これがグループ モーメントになります (is_groupmoment = True のトレーニング モーメントと、特定のグループにリンクする多対多フィールド)。アスリートがグループモーメントもあるモーメント番号と日付にグループモーメントを計画している場合、彼自身のモーメントはこのモーメントを上書きする必要があります。

実用的で非常に単純化された例

私には次の個々の瞬間があります: 日、瞬間nr 月曜日 1 火曜日 1

そして次のグループモーメント: Day, Momentr 火曜日 1 水曜日 1

瞬間とこれらの瞬間に関連する活動の合計を示す表では、月曜日と火曜日の個々の瞬間だけでなく、グループの瞬間を上書きする個人の瞬間がないため、水曜日のグループの瞬間も表示したいと思います。グループの瞬間と個々の瞬間の両方が同じ日付の瞬間にあるかどうかを確認しながら、瞬間を1つずつ合計することにより、純粋なPythonコードを使用してビューで合計を行うことができますが、これは本当に醜く遅い方法ですそれは、特に 1 年を合計する場合に顕著です。

私が試したこと

私はこれを試しました:

training_moments = TrainingMoment.objects.filter(date__range=('2013-08-19','2013-10-28'))
moments_to_exclude = training_moments.annotate(num_dates=Count('date'), num_momentnrs=Count('momentnr')).filter(num_dates__gt= 1, num_momentnrs__gt=1)

これは近いです。これにより、「タイムスロット」ごとに1つの瞬間が得られます(瞬間番号と日付は、グループの瞬間と個々の瞬間で同じです)。問題は、両方の「衝突」の瞬間を取得する必要があることです。次に、結果のクエリ セットからグループ モーメントを除外し、最終的にアプリでトレーニング モーメントを合計するときに除外するモーメントを取得できます。

moments_to_exclude = moments_to_exclude.exclude(is_group_moment=True)
desired_result = training_moments.exclude(pk__in=moments_to_exclude)
4

1 に答える 1

2

このような

TrainingMoment.objects.filter(date=… , moment_nr=…).exclude(is_group_moment=True)

django docs のモデル API について少し読んで、manage.py shell

編集

したがって、これは完璧な解決策ではないかもしれませんが、確かにあなたが望む方向です。必要なのは、Moment と Trainee の 2 つのクラスに分割することです。研修生は、1 つのブール フィールドと 1 つの外部キーを一時的に持ちます。

class Moment(models.Model):
    date = …
    number = …

class Trainee(models.Model):
    is_group = models.BooleanField()
    moment = models.ForeignKey(Moment)

ここで得られるのは、同じ日付 + 数値フィールドを持つ重複する Moment オブジェクトがないことです。そのため、頭を悩ませることなく、範囲を簡単にフィルタリングできます。同時に、True 値でフィルタリングする方が簡単で (メソッドを使用して行う)、クライアントのサイズで行うことができます (関連する研修生の数を確認するだけでも)。

同じ情報を持つオブジェクトを追加しないようにカスタム制約を追加する必要がありますが、それはそれほど複雑ではありません

編集 2

コメントに答えるために、テーブルの例を見てみましょう。現在のレイアウトが次のようになっているとします。

╔════════════════════════════════════════╗
║ date       moment_nr is_group_moment   ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True              ║
║ 2013-10-02 1         False             ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True              ║
║ 2013-10-04 2         False             ║
╚════════════════════════════════════════╝

次に、私の設計提案では、次のようになります。

╔════════════════════════════════════════╗
║ date       moment_nr related trainees  ║
╠════════════════════════════════════════╣
║ 2013-10-01 1         True              ║
║ 2013-10-02 1         True, False       ║
║ 2013-10-03 1         False             ║
║ 2013-10-03 2         False             ║
║ 2013-10-04 2         True, False       ║
╚════════════════════════════════════════╝

行が重複していないためです。つまり、以前の設計の 2 つの Moment オブジェクトが実際にマージされ、関連する 2 つの Trainee オブジェクトを持つ 1 つのオブジェクトになります。

つまり、質問の最後からの最終的なコードは次のようになります。

# 1. Get training moments for a given period, for example a week (no problem here)
tms_for_summing = TrainingMoment.objects.filter(date__range=(start_date,end_date))    

# 3. Use the resulting queryset (tms_for_summing) to sum activity
summed_activity = Activity.objects.filter(trainingmoment__in = tms_for_summing)

率直に言って、これは 1 行に短縮することもできます。

summed = Activity.objects.filter(trainingmoment__range=(start_date, end_date))
于 2013-10-21T10:50:41.087 に答える