জ্যাঙ্গোতে গণনা টীকা দেওয়ার জন্য কীভাবে অবজেক্টগুলি ফিল্টার করবেন?


123

সাধারণ জ্যাঙ্গো মডেলগুলি বিবেচনা করুন Eventএবং Participant:

class Event(models.Model):
    title = models.CharField(max_length=100)

class Participant(models.Model):
    event = models.ForeignKey(Event, db_index=True)
    is_paid = models.BooleanField(default=False, db_index=True)

অংশগ্রহণকারীদের মোট সংখ্যার সাথে ইভেন্টের ক্যোয়ারীটি বয়ান করা সহজ:

events = Event.objects.all().annotate(participants=models.Count('participant'))

ফিল্টারযুক্ত অংশগ্রহণকারীদের গণনার সাথে কীভাবে বেনিফিট করবেন is_paid=True?

অংশগ্রহণকারীদের সংখ্যা নির্বিশেষে আমাকে সমস্ত ইভেন্টগুলি জিজ্ঞাসা করতে হবে , উদাহরণস্বরূপ এনটোটেড ফলাফলের মাধ্যমে ফিল্টার করার দরকার নেই। যদি 0অংশগ্রহণকারীরা থাকে, তবে এটি ঠিক আছে, আমার কেবল 0এনোটেটেড মান দরকার ।

নথিপত্র থেকে উদাহরণ এখানে কাজ করে না, কারণ এটি বাদ তাঁদের সঙ্গে টিকা পরিবর্তে কোয়েরি থেকে বস্তু 0

হালনাগাদ. জাজানো 1.8 এর নতুন শর্তাধীন এক্সপ্রেশন বৈশিষ্ট্য রয়েছে , তাই এখন আমরা এটির মতো করতে পারি:

events = Event.objects.all().annotate(paid_participants=models.Sum(
    models.Case(
        models.When(participant__is_paid=True, then=1),
        default=0,
        output_field=models.IntegerField()
    )))

আপডেট 2. জাজানো 2.0 তে নতুন শর্তাধীন সমষ্টি বৈশিষ্ট্য রয়েছে, নীচে গৃহীত উত্তরটি দেখুন।

উত্তর:


105

জাজানো ২.০-এর শর্তসাপেক্ষ সমষ্টি আপনাকে অতীতে যে পরিমাণ ত্রুটি করেছিল তা আরও হ্রাস করতে দেয়। এটি পোস্টগ্র্রেসের filterযুক্তিও ব্যবহার করবে , যা একটি যোগফলের চেয়ে কিছুটা দ্রুত (আমি চারপাশে ২০-৩০% ব্যান্ডডের মতো সংখ্যা দেখেছি)।

যাইহোক, আপনার ক্ষেত্রে, আমরা এর মতো সহজ কিছু খুঁজছি:

from django.db.models import Q, Count
events = Event.objects.annotate(
    paid_participants=Count('participants', filter=Q(participants__is_paid=True))
)

টিকাগুলিতে ফিল্টারিং সম্পর্কে ডক্সে একটি পৃথক বিভাগ রয়েছে । এটি শর্তাধীন সমষ্টি হিসাবে একই জিনিস তবে উপরের আমার উদাহরণের মতো more যেভাবেই হোক না কেন, এটি আমি আগে যা করছিলাম তার তুলনায় অনেক বেশি স্বাস্থ্যকর।


বিটিডাব্লু, ডকুমেন্টেশন লিঙ্কের মতো কোনও উদাহরণ নেই, কেবল aggregateব্যবহার দেখানো হয়েছে। আপনি কি ইতিমধ্যে এ জাতীয় প্রশ্নগুলি পরীক্ষা করেছেন? (আমি নেই এবং আমি বিশ্বাস করতে চাই! :)
রুড্রাইক

2
আমার আছে. তারা কাজ করে. আমি আসলে একটি অদ্ভুত প্যাচ মারলাম যেখানে একটি পুরানো (সুপার-জটিল) সাবকোয়ারি জ্যাঙ্গো ২.০ এ আপগ্রেড করার পরে কাজ করা বন্ধ করে দিয়েছে এবং আমি এটিকে একটি অতি-সহজ ফিল্টারড কাউন্ট দিয়ে প্রতিস্থাপন করতে সক্ষম হয়েছি। টীকাগুলির জন্য আরও একটি ডক উদাহরণ রয়েছে তাই আমি এখনই এটি টানব।
অলি

1
এখানে কয়েকটি উত্তর রয়েছে, এটি জ্যাঙ্গো ২.০ উপায় এবং নীচে আপনি জ্যাঙ্গো 1.11 (সাবকিউরিস) উপায় এবং জ্যাঙ্গো 1.8 উপায় পাবেন way
রায়ান কাস্টনার

2
সাবধান, আপনি যদি এটি জ্যাঙ্গো <2 চেষ্টা করুন, 1.9 the, এটা হবে ব্যতিক্রম ছাড়াই চালানো, কিন্তু ফিল্টার কেবল প্রয়োগ করা হয় না। সুতরাং এটি জ্যাঙ্গো <2 এর সাথে কাজ করার জন্য উপস্থিত হতে পারে তবে তা তা করে না।
djvg

যদি আপনাকে একাধিক ফিল্টার যুক্ত করতে হয় তবে আপনি এগুলি পৃথক করে Q () যুক্তিতে যুক্ত করতে পারেন, উদাহরণস্বরূপ ফিল্টার = কিউ (অংশগ্রহণকারীদের_আইস_পেইড = সত্য,
সামথিনজেলস

93

সবেমাত্র আবিষ্কার হয়েছে যে জাঙ্গো 1.8 এর নতুন শর্তাধীন এক্সপ্রেশন বৈশিষ্ট্য রয়েছে , তাই এখন আমরা এটি করতে পারি:

events = Event.objects.all().annotate(paid_participants=models.Sum(
    models.Case(
        models.When(participant__is_paid=True, then=1),
        default=0, output_field=models.IntegerField()
    )))

যখন ম্যাচিং আইটেমগুলি অনেক হয় তখন এটি কি উপযুক্ত সমাধান? আমাদের বলুন যে আমি সর্বশেষ সপ্তাহে ঘটে যাওয়া ক্লিক ইভেন্টগুলি গণনা করতে চাই।
SverkerSbrg

কেন না? মানে, কেন তোমার কেস আলাদা? উপরের ক্ষেত্রে ইভেন্টে প্রদত্ত অংশগ্রহনকারীদের দ্বারা প্রচুর হতে পারে।
rudyryk

আমার মনে হয় @ সার্ভার এসআরজি যে প্রশ্নটি জিজ্ঞাসা করছে তা হ'ল এটি বড় সেটগুলির পক্ষে অকার্যকর, এটি কাজ করবে কি না তার চেয়ে .... সঠিক? সবচেয়ে গুরুত্বপূর্ণ বিষয়টি হ'ল এটি অজগরটিতে এটি করছে না, এটি একটি এসকিউএল কেস ক্লজ তৈরি করছে - github.com/django/django/blob/master/django/db/models/… দেখুন - - সুতরাং এটি যুক্তিসঙ্গতভাবে অভিনয় করবেন, যোগদানের চেয়ে সহজ উদাহরণটি আরও ভাল, তবে আরও জটিল সংস্করণগুলিতে সাবকোয়ারি ইত্যাদি অন্তর্ভুক্ত থাকতে পারে
হেডেন ক্রকার

1
এটির Countপরিবর্তে (পরিবর্তে Sum) ব্যবহার করার সময় আমি অনুমান করি যে আমাদের সেট করা উচিত default=None(যদি না জাজানো 2 টি filterআর্গুমেন্ট ব্যবহার না করা হয় )।
djvg

41

হালনাগাদ

আমি যে সাব-কোয়েরি পদ্ধতির উল্লেখ করেছি তা এখন জ্যাঙ্গো 1.11-এ সাব-কোয়েরি -এক্সপ্রেশন দ্বারা সমর্থিত ।

Event.objects.annotate(
    num_paid_participants=Subquery(
        Participant.objects.filter(
            is_paid=True,
            event=OuterRef('pk')
        ).values('event')
        .annotate(cnt=Count('pk'))
        .values('cnt'),
        output_field=models.IntegerField()
    )
)

আমি এটি সর্বোপরি সমষ্টিকে (সমষ্টি + কেস) পছন্দ করি , কারণ এটি দ্রুততর এবং সহজতর হওয়া উচিত (যথাযথ সূচক সহ)

পুরানো সংস্করণের জন্য, একই ব্যবহার করে অর্জন করা যেতে পারে .extra

Event.objects.extra(select={'num_paid_participants': "\
    SELECT COUNT(*) \
    FROM `myapp_participant` \
    WHERE `myapp_participant`.`is_paid` = 1 AND \
            `myapp_participant`.`event_id` = `myapp_event`.`id`"
})

ধন্যবাদ টডর! দেখে মনে হচ্ছে আমি ব্যবহার না করেই উপায়টি খুঁজে পেয়েছি .extra, কারণ আমি জাজানোতে এসকিউএল এড়ানো পছন্দ করি :) আমি প্রশ্নটি আপডেট করব।
rudyryk

1
আপনার স্বাগত, বিটিডব্লিউ আমি এই পদ্ধতির বিষয়ে অবহিত, তবে এটি এখন অবধি কার্যকর ছিল না, এ কারণেই আমি এ সম্পর্কে উল্লেখ করি নি। তবে আমি সবেমাত্র খুঁজে পেয়েছি যে এটি ঠিক করা হয়েছে Django 1.8.2, সুতরাং আমি অনুমান করি যে আপনি সেই সংস্করণটির সাথে আছেন এবং সে কারণেই এটি আপনার পক্ষে কাজ করছে। আপনি এখানে এবং এখানে
টডর

2
আমি পেয়ে যাচ্ছি যে এটি 0 হওয়ার আগে কোনওটিই তৈরি করে না অন্য কেউ এটি পেয়েছে?
স্টিফানজোলিয়ার

@ স্টেফান জে কলিয়ার হ্যাঁ, আমিও পেয়েছি None। আমার সমাধানটি Coalesce( from django.db.models.functions import Coalesce) ব্যবহার করা ছিল । আপনি এটা ভালো ব্যবহার করুন: Coalesce(Subquery(...), 0)। যদিও এর চেয়ে আরও ভাল উপায় থাকতে পারে।
অ্যাডাম টেলর

6

আমি পরিবর্তে .valuesআপনার Participantক্যোয়ারসেটের পদ্ধতিটি ব্যবহার করার পরামর্শ দেব ।

সংক্ষেপে, আপনি যা করতে চান তা দ্বারা দেওয়া হয়েছে:

Participant.objects\
    .filter(is_paid=True)\
    .values('event')\
    .distinct()\
    .annotate(models.Count('id'))

একটি সম্পূর্ণ উদাহরণ অনুসরণ করা হয়:

  1. 2 Eventটি তৈরি করুন :

    event1 = Event.objects.create(title='event1')
    event2 = Event.objects.create(title='event2')
  2. যোগ Participantতাদের S:

    part1l = [Participant.objects.create(event=event1, is_paid=((_%2) == 0))\
              for _ in range(10)]
    part2l = [Participant.objects.create(event=event2, is_paid=((_%2) == 0))\
              for _ in range(50)]
  3. Participantতাদের eventক্ষেত্র অনুসারে সমস্ত গ্রুপ করুন :

    Participant.objects.values('event')
    > <QuerySet [{'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, '...(remaining elements truncated)...']>

    এখানে স্বতন্ত্র প্রয়োজন:

    Participant.objects.values('event').distinct()
    > <QuerySet [{'event': 1}, {'event': 2}]>

    এখানে .valuesএবং কী .distinctকরছে তা হচ্ছে তারা Participantতাদের উপাদান দ্বারা গোষ্ঠীযুক্ত দুটি বালতি তৈরি করছে event। নোট করুন যে বালতিগুলি রয়েছে Participant

  4. তারপরে সেই বালতিগুলিতে মূল সেটটি থাকায় আপনি এটিকে টীকায়িত করতে পারেন Participant। এখানে আমরা সংখ্যা গণনা করতে চাই Participant, এটি কেবল idসেই বালতিগুলিতে থাকা উপাদানগুলির সংখ্যা গণনা করে করা হয় (যেহেতু সেগুলি হয় Participant):

    Participant.objects\
        .values('event')\
        .distinct()\
        .annotate(models.Count('id'))
    > <QuerySet [{'event': 1, 'id__count': 10}, {'event': 2, 'id__count': 50}]>
  5. অবশেষে আপনি কেবল Participantএকটি is_paidসত্তার সাথে চান True, আপনি কেবল পূর্বের এক্সপ্রেশনটির সামনে একটি ফিল্টার যুক্ত করতে পারেন এবং এটির উপরে বর্ণিত এক্সপ্রেশন:

    Participant.objects\
        .filter(is_paid=True)\
        .values('event')\
        .distinct()\
        .annotate(models.Count('id'))
    > <QuerySet [{'event': 1, 'id__count': 5}, {'event': 2, 'id__count': 25}]>

একমাত্র ত্রুটিটি হ'ল উপরের পদ্ধতি থেকে Eventআপনার কেবল যেমনটি রয়েছে তাই আপনাকে পরে পুনরুদ্ধার করতে হবে id


2

আমি কী ফলাফল খুঁজছি:

  • লোকেরা (অ্যাসিগনি) যাদের কাজগুলিতে একটি প্রতিবেদনে যুক্ত হয়। - জনগণের মোট অনন্য গণনা
  • যে সমস্ত লোকের কার্য রয়েছে সেগুলি একটি প্রতিবেদনে যুক্ত হয়েছে তবে, সেই কাজের জন্য যার দায়বদ্ধতা কেবল 0 এর বেশি।

সাধারণভাবে, আমাকে দুটি পৃথক প্রশ্ন ব্যবহার করতে হবে:

Task.objects.filter(billable_efforts__gt=0)
Task.objects.all()

তবে আমি উভয়ই এক প্রশ্নের মধ্যে চাই। অত: পর:

Task.objects.values('report__title').annotate(withMoreThanZero=Count('assignee', distinct=True, filter=Q(billable_efforts__gt=0))).annotate(totalUniqueAssignee=Count('assignee', distinct=True))

ফলাফল:

<QuerySet [{'report__title': 'TestReport', 'withMoreThanZero': 37, 'totalUniqueAssignee': 50}, {'report__title': 'Utilization_Report_April_2019', 'withMoreThanZero': 37, 'totalUniqueAssignee': 50}]>
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.