Django 1.9.5:偶尔出现反向查找字段的 FieldError

Posted

技术标签:

【中文标题】Django 1.9.5:偶尔出现反向查找字段的 FieldError【英文标题】:Django 1.9.5: FieldError for reverse lookup fields occasionally 【发布时间】:2017-06-12 14:55:31 【问题描述】:

当我尝试运行 objects.all() 以获取预取的 Queryset 时,我在 Django 中遇到了令人沮丧的间歇性错误。在查询集的实例化和通过它运行迭代之间,有时model._meta 似乎缺少字段。就好像查询集的预取实际上并没有在对象列表的迭代中及时运行。

在此示例中,data.service_log 只是一个查询集,其中包含一些名为 servicelog 的预取项。当我在 shell 中运行查询集时,我可以查看查询集上 self.names_to_path(lookup_splitted, self.get_meta()) 方法中的所有字段。它们都在那里,特别是“服务日志”。

请注意此错误的 Traceback,它表示“服务日志”不是可用字段,但它会将其列在可供选择的字段列表中。这似乎是一个 Django 错误,但我无法确定,因为我无法解释或隔离该行为。我不可能是唯一遇到此错误的人。它似乎在django/db/models/sql/query.py 中的names_to_paths() 方法中。以下是无法解析的代码:

query.py names_to_paths():

field = None
try:
    field = opts.get_field(name)
except FieldDoesNotExist:
    if name in self.annotation_select:
        field = self.annotation_select[name].output_field

if field is not None:
    # Fields that contain one-to-many relations with a generic
    # model (like a GenericForeignKey) cannot generate reverse
    # relations and therefore cannot be used for reverse querying.
    if field.is_relation and not field.related_model:
        raise FieldError(
            "Field %r does not generate an automatic reverse "
            "relation and therefore cannot be used for reverse "
            "querying. If it is a GenericForeignKey, consider "
            "adding a GenericRelation." % name
        )
    try:
        model = field.model._meta.concrete_model
    except AttributeError:
        model = None
else:
    # We didn't find the current field, so move position back
    # one step.
    pos -= 1
    if pos == -1 or fail_on_missing:
        field_names = list(get_field_names_from_opts(opts))
        available = sorted(field_names + list(self.annotation_select))
        raise FieldError("Cannot resolve keyword %r into field. "
                         "Choices are: %s" % (name, ", .join(available)))
    break

field does not get set in the first try, then the condition statementif 字段不是 Nonefails so we enter theelseblock. There theposgets reduced by one, but since this field is 'servicelog' it is already at 0. However, when I try this in the shell, it always finds thefieldwithopts.get_field('servicelog')`。只有从 WSGI 和 Apache2 运行时才会发生此故障。同样,它并非一直如此,这使得测试变得非常困难。我对此感到困惑,不知道在哪里寻找线索。请如果有人对探索什么有任何想法,我将不胜感激。

Traceback (most recent call last):

  File "/var/www/fast/services/views/edit.py", line 12897, in service_log
    for service in data.service_log:

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 258, in __iter__
    self._fetch_all()

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1076, in _fetch_all
    self._prefetch_related_objects()

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 656, in _prefetch_related_objects
    prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1457, in prefetch_related_objects
    obj_list, additional_lookups = prefetch_one_level(obj_list, prefetcher, lookup, level)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1556, in prefetch_one_level
    prefetcher.get_prefetch_queryset(instances, lookup.get_current_queryset(level)))

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/fields/related_descriptors.py", line 802, in get_prefetch_queryset
    queryset = queryset._next_is_sticky().filter(**query)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 790, in filter
    return self._filter_or_exclude(False, *args, **kwargs)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 808, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1243, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1269, in _add_q
    allow_joins=allow_joins, split_subq=split_subq,

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1149, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg)

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1035, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())

  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/query.py", line 1330, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))

FieldError: Cannot resolve keyword u'servicelog' into field. Choices are: additional_county_worker_notes, adoption_disrupted, adoption_first_name, adoption_last_name, adoption_middle_name, adoption_placement_date, adoption_placement_reason, adoption_placement_reason_id, adoption_placement_type, adoption_risk_level, adoption_risk_level_id, adoption_termination_date, adoption_termination_destination, adoption_termination_reason, adoption_termination_reason_id, adoptive_placement, agency, agency_id, all_items_outstanding, all_items_past_due, appeal_process_date, attached_file, attends_college, attorney_email_address, attorney_email_address_id, attorney_fax, attorney_fax_id, attorney_investigator_email_address, attorney_investigator_email_address_id, attorney_investigator_fax, attorney_investigator_fax_id, attorney_investigator_name, attorney_investigator_phone, attorney_investigator_phone_id, attorney_name, attorney_phone, attorney_phone_id, blood_related_to_applicants, blood_relationship, casa_email_address, casa_email_address_id, casa_fax, casa_fax_id, casa_name, casa_phone, casa_phone_id, certification_items_outstanding, certification_items_past_due, classification, classification_id, client, client_id, county_adoption_worker, county_adoption_worker_cell, county_adoption_worker_cell_id, county_adoption_worker_email_address, county_adoption_worker_email_address_id, county_adoption_worker_fax, county_adoption_worker_fax_id, county_adoption_worker_office, county_adoption_worker_office_id, county_adoption_worker_phone, county_adoption_worker_phone_id, county_adoption_worker_title, county_case_number, county_worker, county_worker_cell, county_worker_cell_id, county_worker_email_address, county_worker_email_address_id, county_worker_fax, county_worker_fax_id, county_worker_office, county_worker_office_id, county_worker_phone, county_worker_phone_id, county_worker_title, court, court_case_name, court_case_number, court_department, court_id, created, current_grade, date_identified_adoptive, date_placed_with_home, deleted, discharge_summary, eligibility_worker, eligibility_worker_email_address, eligibility_worker_email_address_id, eligibility_worker_phone, eligibility_worker_phone_id, emergency_placement, employed_80_hours, enable_discharge_summary, expected_type, expected_type_id, extended_family_contact_allowed, final_payment_amount, finalization_date, foreign_placement, hearing_36626_date, homestudy, id, incident_placement_1, incident_placement_2, incident_placement_3, incident_placement_4, individualized_plan_review, inhousemove, interpretive_summary, item_due, items_approved, items_pending, items_rejected, items_update_requested, la_county_id, medi_cal, medi_cal_eligibility_phone, medi_cal_eligibility_phone_id, medi_cal_eligibility_worker, medi_cal_id, modified, monthly_monitored_visit_hours, mother_child, move_in_type, move_out_type, new_protective_custody_petition, non_minor_dependent, non_truant, notes, number_of_files_required, other_school_contact, other_school_contact_first_name, other_school_contact_last_name, parent_payment_override_annually, parent_payment_override_daily, parent_payment_override_monthly, parental_contact_allowed, parental_group, parental_group_id, payment_amount, percent_certified, percent_items_complete, person_number, placement, placement_date, placement_id, placement_payment_override_annually, placement_payment_override_daily, placement_payment_override_monthly, placement_reason, placement_reason_details, placement_reason_id, placement_self, placer_shelter_bed, prior_placement, progress_summary, projected_adoption_36626_date, projected_adoption_finalization_date, projected_adoption_placement_date, recordreview, requires_educational_support, requires_mental_health_services, respite, school, school_different, school_id, school_liaison_email, school_liaison_first_name, school_liaison_last_name, school_liaison_phone, school_liaison_phone_extension, school_notes, serial_number, servicecontact_onbehalf, servicedeliverylog, servicelog, social_worker_at_termination, social_worker_at_termination_id, special_health_care_needs, state_case_number, teachers, termination_date, termination_destination, termination_reason, termination_reason_details, termination_reason_id, therapist, therapy_code, therapy_supervision_requirements, treatment_abilities, treatment_needs, treatment_preferences, treatment_strengths, treatmentneed, update_requested, update_requested_by, update_requested_by_id, update_requested_date, update_requested_note, updateable, use_number_required, uses_psychotropic_medication, visit_frequency_override, visit_frequency_override_id, visitation_restrictions, who_can_pickup_at_home, who_can_pickup_at_school, who_can_visit

更新 - 添加模型/违规视图代码

models.py
class ParentalGroup(models.Model):
    many fields...

class Placement(models.Model):
    parental_group = models.ForeignKey(ParentalGroup, null=True, blank=True)
    many more fields...

class ServiceLog(models.Model):
    parental_group = models.ForeignKey(ParentalGroup, null=True, blank=True)
    placement = models.ManyToManyField(Placement, blank=True)
    many more fields...


views.py:
data.service_log = ServiceLog.objects.filter(
    parental_group=data.pg,
).prefetch_related(
    Prefetch(
        'placement',
        queryset=Placement.objects.all(),
        to_attr='placements'
    ),
)

for service in data.service_log:
    some code to generate data to pass to template...

return render_to_response(...)

【问题讨论】:

你能分享你的模型吗? 我认为这不会有帮助,但可以肯定。我将添加模型。 【参考方案1】:

我在 Gunicorn/Django 运行服务器上使用 Django 1.8.6 时遇到了类似的问题。我也没有设法在 shell/notebook 环境中重现错误。

我通过将related_name 添加到ManyToManyField 来解决随机发生的FieldError。我在 ManyToManyField 中使用了a through model。

在你的情况下:

class ServiceLog(models.Model):
    parental_group = models.ForeignKey(ParentalGroup, null=True, blank=True)
    placement = models.ManyToManyField(Placement, blank=True, related_name='servicelog')

一月

【讨论】:

嗯,感谢您的回复。听起来可能会奏效。我通常不会添加相关名称,因为 Django 会自动添加。如果它似乎有效,我会尝试并接受解决方案。可能要过几天我才能确定。 到目前为止,它已经有了很大的改进。这两天我已经收到两三个了。每天20-30个。我将继续监测一周左右。非常感谢您的帮助,如果这能解决问题! 我们也随机遇到这个问题,请解释一下这个问题的原因?

以上是关于Django 1.9.5:偶尔出现反向查找字段的 FieldError的主要内容,如果未能解决你的问题,请参考以下文章

Django:内置组件Content-Type

ngnix反向代理tomcat偶尔出现请求50x.html错误

Django:提要的反向查找URL?

Django:prefetch_related() 是不是遵循反向关系查找?

Django 反向查找外键

Django 通过 ForeignKey 反向查找