Django:连续查询集过滤在第一次查询后停止过滤

时间:2019-01-10 18:28:14

标签: django django-orm

下面的调试会话说明了我想说的话。 我已经在Django开发服务器,Cherrypy服务器和Werkzeug调试器下执行了此操作。 在Windows 10 MySQL 5.5后端上使用Django 1.11.10。

该函数(在本文结尾)对QuerySet进行连续过滤。 我停止执行程序以执行一些健全性检查。 我发现,第一个过滤器操作的工作是event_values.count()== 14,但是任何进一步的过滤尝试将始终产生相同的结果。 例如,我在pk = 72上进行过滤,但仍然返回了所有14个模型实例。 我尝试使用get(pk = 72)并得到了MultipleObjectsReturned异常。

有趣的是,当我在Django shell中执行连续过滤时,我得到了应该得到的结果,但是当我从shell中运行实际代码时,它也有同样的问题。

有人可以让我知道我在做什么错吗?

谢谢。

(Pdb) l
160                 event_values = event_values.filter(
161                     tsn__gte=tsn_start, tsn__lte=tsn_end)
162
163             import pdb; pdb.set_trace()
164
165  ->         if tso_start or tso_end:
166                 tso_start, tso_end = resolve_ranges(tso_start, tso_end)
167
168                 event_values = event_values.filter(
169                     tso__gte=tso_start, tso__lte=tso_end)
170
(Pdb) tsn_start
5800
(Pdb) tsn_end
6000
(Pdb) event_values.count()
14
(Pdb) event_values
<QuerySet [<ComplianceEvent: pk: 72 -- 383201 -- 2017-12-11 -- SB>, <ComplianceEvent: pk: 73 -- 383202 -- 2017-04-11 -- SB>, <ComplianceEvent: pk: 74 -- 383203 -- 2018-01-15 -- SB>, <ComplianceEvent: pk: 75 -- 383210 -- 2017-10-06 -- SB>, <ComplianceEvent: pk: 76 -- 383217 -- 2018-05-18 -- SB>, <ComplianceEvent: pk: 77 -- 383219 -- 2017-09-21 -- SB>, <ComplianceEvent: pk: 78 -- 383303 -- 2017-07-19 -- SB>, <ComplianceEvent: pk: 79 -- 383303 -- 2017-07-07 -- SB>, <ComplianceEvent: pk: 80 -- 383309 -- 2018-02-23 -- SB>, <ComplianceEvent: pk: 81 -- 383313 -- 2018-03-07 -- SB>, <ComplianceEvent: pk: 82 -- 383315 -- 2016-04-18 -- SB>, <ComplianceEvent: pk: 83 -- 383317 -- 2017-04-21 -- SB>, <ComplianceEvent: pk: 84 -- 383319 -- 2018-02-22 -- SB>, <ComplianceEvent: pk: 85 -- 383324 -- 2018-08-15 -- SB>]>
(Pdb) event_values.filter(pk=72)
<QuerySet [<ComplianceEvent: pk: 72 -- 383201 -- 2017-12-11 -- SB>, <ComplianceEvent: pk: 73 -- 383202 -- 2017-04-11 -- SB>, <ComplianceEvent: pk: 74 -- 383203 -- 2018-01-15 -- SB>, <ComplianceEvent: pk: 75 -- 383210 -- 2017-10-06 -- SB>, <ComplianceEvent: pk: 76 -- 383217 -- 2018-05-18 -- SB>, <ComplianceEvent: pk: 77 -- 383219 -- 2017-09-21 -- SB>, <ComplianceEvent: pk: 78 -- 383303 -- 2017-07-19 -- SB>, <ComplianceEvent: pk: 79 -- 383303 -- 2017-07-07 -- SB>, <ComplianceEvent: pk: 80 -- 383309 -- 2018-02-23 -- SB>, <ComplianceEvent: pk: 81 -- 383313 -- 2018-03-07 -- SB>, <ComplianceEvent: pk: 82 -- 383315 -- 2016-04-18 -- SB>, <ComplianceEvent: pk: 83 -- 383317 -- 2017-04-21 -- SB>, <ComplianceEvent: pk: 84 -- 383319 -- 2018-02-22 -- SB>, <ComplianceEvent: pk: 85 -- 383324 -- 2018-08-15 -- SB>]>
(Pdb) event_values.get(pk=72)
*** esipeht.models.MultipleObjectsReturned: get() returned more than one ComplianceEvent -- it returned 14!
(Pdb) ComplianceEvent.objects.get(pk=72)
<ComplianceEvent: pk: 72 -- 383201 -- 2017-12-11 -- SB>

完整代码:

def evaluate_search(criteria):
    """Execute the query and return associated engine events.

    The submitted dict:
    {
    'ac_rh_end': None,
    'ac_rh_start': None,
    'affected_parts': ['31'],
    'aircraft': [],
    'end_date': None,
    'engines': [],
    'event_source': '',
    'event_subtype': ['Compliance-SB'],
    'positions': [],
    'ref_docs': [],
    'ress': [],
    'start_date': None,
    'status': '',
    'tsn_end': 6000,
    'tsn_start': 5800,
    'tso_end': None,
    'tso_start': None
    }    

    Args:
        criteria (dict): The criteria posted by the user.

    Returns:
        The associated engine events.
    """

    ac_rh_end = criteria['ac_rh_end']
    ac_rh_start = criteria['ac_rh_start']
    end_date = criteria['end_date']
    start_date = criteria['start_date']
    event_source = criteria['event_source']
    tsn_end = criteria['tsn_end']
    tsn_start = criteria['tsn_start']
    tso_end = criteria['tso_end']
    tso_start = criteria['tso_start']
    review_status = criteria['status']

    # get ids of the form multiselects
    # note: if the criteria was not selected, the result will be an empty list
    affpart_ids = criteria['affected_parts']
    aircraft = criteria['aircraft']
    engines = criteria['engines'] 
    ref_doc_ids = criteria['ref_docs']
    res_ids = criteria['ress']
    positions = criteria['positions']

    event_subtype = criteria['event_subtype']

    # create a dictionary with all subtypes initialized to empty querysets
    events = {
        'Fitment' : FitmentEvent.objects.none(),
        'Compliance' : ComplianceEvent.objects.none(), 
        'Incident' : IncidentEvent.objects.none(),
        'RepairOverhaul' :  RepairOverhaulEvent.objects.none()
    }

    # if one or more event subtypes were selected by user
    if event_subtype:
        # determine event and event subtype
        for type_subtype in event_subtype:
            event_type, subtype = type_subtype.split('-')

            model_class = getattr(esipeht.models, event_type + 'Event')

            events[event_type] |= model_class.objects.filter(
                event_subtype=subtype
            )

    # if no subtypes selected then we want everything
    else:
        if affpart_ids:
            # Fitment events do not reference affected parts            
            events['Fitment'] = FitmentEvent.objects.none()

        else:
            events['Fitment'] = FitmentEvent.objects.all()

        events['Compliance'] = ComplianceEvent.objects.all()

        events['Incident'] = IncidentEvent.objects.all()

        events['RepairOverhaul'] = RepairOverhaulEvent.objects.all()

    results = []

    # keep track of the number of each type of event
    all_counts = {
        'Fitment' : 0,
        'Compliance' : 0,
        'Incident' : 0,
        'RepairOverhaul' : 0
    }

    # loop through the query sets (event_values) for each event type (key)
    for event_type, event_values in events.items(): 
        if event_values.count() == 0:
            continue

        if engines:
            event_values = event_values.filter(
                engine__serial_number__in=engines)

        if aircraft:
            event_values = event_values.filter(
                aircraft__tail_number__in=aircraft)

        if positions:
            event_values = event_values.filter(engine_position__in=positions)

        # a fitment event does not reference affected parts
        if affpart_ids and event_type != 'Fitment':
            event_values1 = event_values.filter(
                affected_parts__id__in=affpart_ids)

            event_values2 = event_values.filter(
                ress__affected_parts__id__in=affpart_ids)

            event_values = event_values1.union(event_values2)

        if ref_doc_ids:
            event_values = event_values.filter(
                reference_documents__id__in=ref_doc_ids)

        if res_ids:
            event_values = event_values.filter(ress__id__in=res_ids)

        if tsn_start or tsn_end:
            tsn_start, tsn_end = resolve_ranges(tsn_start, tsn_end)

            event_values = event_values.filter(
                tsn__gte=tsn_start, tsn__lte=tsn_end)

        import pdb; pdb.set_trace() 

        if tso_start or tso_end:
            tso_start, tso_end = resolve_ranges(tso_start, tso_end)

            event_values = event_values.filter(
                tso__gte=tso_start, tso__lte=tso_end)

        if ac_rh_start or ac_rh_end:
            ac_rh_start, ac_rh_end = resolve_ranges(ac_rh_start, ac_rh_end)

            event_values = event_values.filter(
                aircraft_hours__gte=ac_rh_start, aircraft_hours__lte=ac_rh_end)

        if start_date or end_date:
            start_date, end_date = resolve_ranges(start_date, end_date)

            event_values = event_values.filter(
                event_date__gte=start_date, event_date__lte=end_date)

        if event_source:
            event_values = event_values.filter(source=event_source)

        if review_status:
            event_values = event_values.filter(review_status=review_status)

        results.extend(list(event_values.distinct()))

        all_counts[event_type] += event_values.count()

    sorted_results = sorted(results, key=attrgetter('engine', 'event_date'))

    grouped_events = {}

    for event in sorted_results:
        engine = event.engine.serial_number

        if engine in grouped_events:
            grouped_events[engine].append(event)

        else:
            grouped_events[engine] = [event]

    return grouped_events, all_counts


def resolve_ranges(start, end):
    """This will determine how numeric and date ranges are filtered.
    """

    if start and end:
        # if start and end are reversed, swap them
        if end < start:
            start, end = end, start

        return start, end

    elif start:
        end = (
            datetime.date.today() if type(start) == datetime.date else 999999
        )

        return start, end

    elif end:
        start = (
            datetime.date(2009, 1, 1) if type(end) == datetime.date else 0
        )

        return start, end

    return None, None

1 个答案:

答案 0 :(得分:0)

找到答案:

替换

event_values = event_values1.union(event_values2)

使用

event_values = (event_values1 | event_values2).distinct()

union()方法的评估正确,但是结果查询集无法进一步过滤。

我在以下位置检查了文档

Django Queryset UNION

发现这是设计使然:

此外,仅允许LIMIT,OFFSET,COUNT(*),ORDER BY和指定列(即切片,count(),order_by()和values()/ values_list())。结果QuerySet。