AttributeError: частично инициализированный модуль 'posts.views' не имеет атрибута 'SearchMenuPage' (скорее всего, из-за циклического импорта)
Я пытаюсь эмулировать маршрутизацию url в StackOverflow, когда нужно запросить URL с путем search/
. Как показано, запрос этого URL перенаправляется на search?q=
.
При выполнении теста для достижения вышеупомянутого результата возникает следующая ошибка:
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="