AttributeError: частично инициализированный модуль 'posts.views' не имеет атрибута 'SearchMenuPage' (скорее всего, из-за циклического импорта)

Я пытаюсь эмулировать маршрутизацию url в StackOverflow, когда нужно запросить URL с путем search/. Как показано, запрос этого URL перенаправляется на search?q=.

enter image description here

При выполнении теста для достижения вышеупомянутого результата возникает следующая ошибка:

  • AttributeError: partially initialized module 'posts.views' has no attribute 'SearchMenuPage' (most likely due to a circular import)

Вместо RedirectView я устанавливаю атрибут url в f-строку, которая интерполирует reverse_lazy(). https://docs.djangoproject.com/en/4.1/ref/urlresolvers/#reverse-lazy

Какова причина этой ошибки и что следует предпринять?

class TestRedirectSearchView(TestCase):

    def setUp(self):
        self.request_url = reverse('posts:search_menu')
        self.response_url = f"{reverse('posts:search_results')}?q="

    def test_search_page_response(self):
        response = self.client.get(self.request_url, follow=True)
        self.assertRedirects(response, self.response_url)
posts_patterns = ([
    path("", pv.QuestionListingPage.as_view(), name="main"),
    re_path(r"questions/?", pv.AllQuestionsPage.as_view(), name="main_paginated"),
    re_path(r"questions/ask/?", pv.AskQuestionPage.as_view(), name="ask"),
    re_path(r"questions/<question_id>/edit/?", pv.EditQuestionPage.as_view(), name="edit"),
    re_path("questions/<question_id>/edit/answers/<answer_id>/?", pv.EditPostedAnswerPage.as_view(), name="answer_edit"),
    re_path("questions/<question_id>/?", pv.PostedQuestionPage.as_view(), name="question"),
    re_path(r"questions/search/", pv.SearchMenuPage.as_view(), name="search_menu"),
    re_path(r"questions/search", pv.SearchResultsPage.as_view(), name="search_results"),
    path("questions/tagged/<tags>", pv.TaggedSearchResultsPage.as_view(), name="tagged")
], "posts")
class SearchMenuPage(RedirectView):
    url = f"{reverse_lazy('posts:search_results')}?q="


class SearchResultsPage(PaginatedPage):

    def get(self, request):
        query, tab_index = request.GET.get('q'), request.GET.get('tab', 'newest')
        queryset, query_data = Question.searches.lookup(tab_index, query=query)
        if query_data['tags'] and not query_data['title'] and not query_data['user']:
            tags = "".join([
                f"{tag}+" if i != len(query_data["tags"]) - 1 else f"{tag}"
                for i, tag in enumerate(query_data["tags"])
            ])
            return HttpResponseRedirect(reverse("posts:tagged", kwargs={'tags': tags}))
        else:
            context = super().get_context_data()
            search_value = "".join(list(reduce(
                lambda main_string, dict_item: (
                    main_string + f" {dict_item[0]}:\"{dict_item[1]}\" "
                    if not isinstance(dict_item[1], list) and dict_item[0] == "title" else (
                        main_string + f" {dict_item[0]}:{dict_item[1]} "
                        if not isinstance(dict_item[1], list) and dict_item[0] == "user" else
                        main_string + "".join([f"[{value}] " for value in dict_item[1] if value]).strip()
                    )
                ), list(filter(lambda value: value[1] is not None, query_data.items())), ""
            ))).strip()
            context['search_form'].fields['q'].widget.attrs.update(
                {"value": search_value}
            )
            context['paginator'].object_list = queryset
            page = context['paginator'].get_page(
                request.GET.get("page", None)
            )
            context.update({
                'title': "Search Results",
                'query_data': query_data,
                'questions': page,
                'page_links': get_page_links(page),
                'count': page.paginator.count
            })
        return self.render_to_response(context)

Traceback:

 File "C:\..\django_stackoverflow\posts\views.py", line 280, in <module>
    class SearchMenuPage(RedirectView):
  File "C:\..\django_stackoverflow\posts\views.py", line 281, in SearchMenuPage
    url = f"{reverse_lazy('posts:search_results')}?q="
  File "C:\..\django\utils\functional.py", line 140, in __text_cast
    return func(*self.__args, **self.__kw)
  File "C:\..\django\urls\base.py", line 54, in reverse
    app_list = resolver.app_dict[ns]
  File "C:\..\django\urls\resolvers.py", line 526, in app_dict
    self._populate()
  File "C:\..\django\urls\resolvers.py", line 460, in _populate
    for url_pattern in reversed(self.url_patterns):
  File "C:\..\django\utils\functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\..\django\urls\resolvers.py", line 598, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "C:\..\django\utils\functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "C:\..\django\urls\resolvers.py", line 591, in urlconf_module
    return import_module(self.urlconf_name)
  File "C:\..\Python39\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "C:\..\stackoverflow_clone\urls.py", line 32, in <module>
    re_path(r"questions/search/", pv.SearchMenuPage.as_view(), name="search_menu"),
AttributeError: partially initialized module 'posts.views' has no attribute 'SearchMenuPage' (most likely due to a circular import)

Применение интерполированной строки с добавлением строки запроса к свойству url вызвало ошибку.

Решение:

class SearchMenuPage(RedirectView):

    pattern_name = "posts:search_results"

    def get_redirect_url(self, *args, **kwargs):
        url = reverse(self.pattern_name)
        return f"{url}?q="

Вернуться на верх