«Запрос на совпадение пользователя не существует», встречающийся в шаблоне, но не в модульном тесте? - PullRequest
0 голосов
/ 08 июня 2018

В проекте Django существует модель Family, определенная следующим образом:

class Family(BaseFamily):
    employee_first_name = models.CharField(max_length=255, blank=True)
    employee_last_name = models.CharField(max_length=255, blank=True)
    partner_first_name = models.CharField(max_length=255, blank=True)
    partner_last_name = models.CharField(max_length=255, blank=True)

, где BaseFamily имеет внешний ключ package и lucy_guide:

class BaseFamily(TimeStampedModel):
    package = models.ForeignKey('lucy_web.Package',
                                models.SET_NULL,
                                blank=True,
                                null=True)
    lucy_guide = models.ForeignKey(
        User,
        blank=True,
        null=True,
        related_name='lucy_guide_%(class)s',
        limit_choices_to={'is_staff': True})

Модель Package, в свою очередь, связана с Company:

class Package(TimeStampedModel):
    company = models.ForeignKey('lucy_web.Company')

, а Company, в свою очередь, имеет lucy_guide и default_package:

class Company(TimeStampedModel):
    lucy_guide = models.ForeignKey(
        User,
        blank=True,
        null=True,
        related_name='lucy_guide_%(class)s',
        limit_choices_to={'is_staff': True})
    default_package = models.OneToOneField(
        Package,
        blank=True,
        null=True,
        related_name='default_for_%(class)s')

Теперь у меня есть шаблон, который использует следующий тег шаблона edit_view_family_heading:

from django import template

register = template.Library()


@register.simple_tag
def edit_view_family_heading(family):
    heading = [family.employee_first_name]

    if family.employee_last_name != family.partner_last_name:
        heading.append(family.employee_last_name)

    heading.extend([
        '&',
        family.partner_first_name,
        family.partner_last_name
    ])

    heading = [e for e in heading if e]

    if heading[0] == '&':
        heading.pop(0)

    if heading and heading[-1] == '&':
        heading.pop()

    if heading:
        if family.lucy_guide:
            heading.append(f'({family.lucy_guide.first_name})')

        return ' '.join(heading)
    else:
        return family.id

Однако, если я пытаюсь отобразить шаблон, я получаю следующееошибка:

Template error:
In template /Users/kurtpeek/Documents/Dev/lucy2/lucy-web/dashboard/templates/dashboard.html, error at line 0
   User matching query does not exist.   1 : {% load static %}
   2 : {% load sass_tags %}
   3 : {% load compress %}
   4 : {% load google_analytics %}
   5 : {% load active_page %}
   6 : 
   7 : <!DOCTYPE html>
   8 : <html lang="en">
   9 : <head>
   10 :   <title>Cleo Admin | {% block page_title %}{% endblock %}</title>


Traceback:

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in __get__
  178.             rel_obj = getattr(instance, self.cache_name)

During handling of the above exception ('Family' object has no attribute '_lucy_guide_cache'), another exception occurred:

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  217.                 response = self.process_exception_by_middleware(e, request)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  215.                 response = response.render()

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/response.py" in render
  107.             self.content = self.rendered_content

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/response.py" in rendered_content
  84.         content = template.render(context, self._request)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/backends/django.py" in render
  66.             return self.template.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render
  207.                     return self._render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  177.             return compiled_parent._render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in _render
  199.         return self.nodelist.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  72.                 result = block.nodelist.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render
  990.                 bit = node.render_annotated(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  957.             return self.render(context)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/template/library.py" in render
  203.         output = self.func(*resolved_args, **resolved_kwargs)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/dashboard/templatetags/edit_view_family_heading.py" in edit_view_family_heading
  28.         if family.lucy_guide:

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in __get__
  184.                 rel_obj = self.get_object(instance)

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py" in get_object
  159.         return qs.get(self.field.get_reverse_related_filter(instance))

File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/venv/lib/python3.6/site-packages/django/db/models/query.py" in get
  380.                 self.model._meta.object_name

Exception Type: DoesNotExist at /dashboard/families/763/case-management
Exception Value: User matching query does not exist.

Я пытался сузить проблему с этими модульными тестами, но все они проходят:

from django.test import TestCase
from django.urls import reverse

from dashboard.tests.utils import AccountMixin
from dashboard.templatetags.edit_view_family_heading import edit_view_family_heading
from lucy_web.test_factories import FamilyFactory, UserFactory
from lucy_web.models import Family


class FamilyEditTest(TestCase, AccountMixin):
    def setUp(self):
        self.login_user(UserFactory(is_superuser=True))

    def test_case_management_with_lucy_guide_defined(self):
        family = FamilyFactory()
        response = self.client.get(
            reverse('dashboard:family', kwargs={'pk': family.id, 'tab': 'case-management'}))
        self.assertTemplateUsed(response, 'families/edit.html')

    def test_case_management_without_lucy_guide_defined(self):
        family = Family()
        family.save()

        self.assertIs(family.lucy_guide, None)

        response = self.client.get(
            reverse('dashboard:family', kwargs={'pk': family.id, 'tab': 'case-management'}))
        self.assertTemplateUsed(response, 'families/edit.html')


class TestFamilyHeading(TestCase):
    def test_edit_view_family_heading(self):
        family = FamilyFactory(
            employee_first_name="Shannon",
            employee_last_name="Spanhake",
            partner_first_name="Aaron",
            partner_last_name="Levie",
            lucy_guide=UserFactory(first_name="Rebekah"))
        heading = edit_view_family_heading(family)
        self.assertEqual(
            heading,
            "Shannon Spanhake & Aaron Levie (Rebekah)")

    def test_edit_view_family_heading_no_lucy_guide(self):
        family = FamilyFactory(package=None)
        self.assertIs(family.lucy_guide, None)
        heading = edit_view_family_heading(family)
        self.assertEqual(
            heading,
            f"{family.employee_first_name} {family.employee_last_name} & "
            f"{family.partner_first_name} {family.partner_last_name}")

Другими словами, представление использует правильный шаблони тег шаблона работает, как и ожидалось, при тестировании в качестве обычной функции, даже если в семействе нет lucy_guide.

Я подозреваю, что моя база данных находится в "странном" состоянии и что где-то в этой взаимосвязанной цепочкеВ моделях чего-то не хватает, но я изо всех сил пытаюсь сузить, что.Любые предложения по устранению этой ошибки?

Обновление

Глядя на базу данных (с pgAdmin 4), может показаться, что рассматриваемый Family имеет lucy_guide_id:

enter image description here

Однако в таблице auth_user нет строки, соответствующей этому id:

enter image description here

Я подозреваю, что это связано с опцией on_delete=models.SET_NULL для внешнего ключа package, но я ничего не вижу в документации (https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey.on_delete) это указывает на то, что любой из параметров позволил бы базе данных войти в такое «поврежденное» состояние.

Есть какие-нибудь идеи, как база данных попала в это состояние и как я мог воспроизвести эту ошибку в модульном тесте?

1 Ответ

0 голосов
/ 08 июня 2018

В итоге я положил вызов family.lucy_guide в блоке try .. except:

    try:
        if family.lucy_guide:
            heading.append(f'({family.lucy_guide.first_name})')
    except ObjectDoesNotExist:
        pass

, где ObjectDoesNotExist импортируется из django.core.exceptions.Страница теперь загружается нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...