Django circular import (models-views(-forms))
Просмотрел все предыдущие темы, посвященные этой проблеме, но так и не нашел правильного решения, поэтому решил создать свой вопрос.
Я создаю проект форума (как часть проекта сайта). Представления сделаны на основе классов: SubForumListView ведет на главную страницу форума, где перечислены его основные разделы («подфорумы»). TopicListView, в свою очередь, ведет на страницы определенных подфорумов со списком активных тем, созданных в рамках данного подфорума. Вид ShowTopic ведет на страницу определенной темы со списком комментариев.
Проблема проявляется из-за:
- Models.py: Метод
get_absolute_url
в моделиSubforum
, который в функцииreverse
секции return принимает вид в качестве 1-го аргумента; я пытался избежать прямого импорта вида, но программа не принимает других вариантов; - Views.py: большинство представлений импортируют модели либо в аргументах экземпляра (
model = Subforum
), либо в методах, использующих кверисеты (как вget_context_data
:topics = Topic.objects.all()
); я не могу с уверенностью сказать, помогает ли замена аргумента экземпляраmodel = Subforum
наmodel = 'Subforum'
, поскольку это невозможно сделать с методами кверисета и поэтому не может быть доказано; - Forms.py: мои классы форм были созданы с помощью
forms.ModelForm
и includeclass Meta
, где аргумент экземпляраmodel
предоставляется так же, как и в 2):model = Topic
. Пока что я закомментировал их (опять же, не будучи уверенным, что это поможет или нет), а также импорт моделей, но когда они были активны, происходил тройной круговой импорт «models-views-forms» (довольно забавно).
Я вижу эту проблему, я знаю, что и где ее провоцирует, но я не знаю, как ее решить, то есть: Я не знаю, как лучше определить представления и формы (или, может быть, модели с их методами «get_absolute_url»), чтобы избежать CI, и как лучше организовать связь между различными частями программы.
Корреспондирующие файлы:
models.py:
views.py:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from core.views import menu
from .forms import AddTopicForm, AddCommentForm
from .models import Subforum, Topic, Comment, Profile
from .utils import DataMixin
class SubForumListView(ListView):
model = Subforum
context_object_name = 'subforum_list'
template_name = "forum/forum.html"
def get_context_data(self, **kwargs):
subforums = Subforum.objects.all()
context = {'subforums': subforums}
return context
class TopicListView(ListView):
model = Topic
template_name = "forum/subforum.html"
slug_url_kwarg = 'subforum_slug'
context_object_name = 'subforum'
def get_context_data(self, **kwargs):
topics = Topic.objects.all()
context = {'topics': topics}
return context
class ShowTopic(DetailView):
model = Topic
template_name = "forum/topic.html"
slug_url_kwarg = 'topic_slug'
context_object_name = 'topic'
def get_context_data(self, topic_slug, **kwargs):
topic = get_object_or_404(Topic, slug=topic_slug)
comments = Comment.objects.filter(topic=topic)
comments_number = len(Comment.objects.filter(topic=topic))
context = {'menu': menu,
'topic': topic,
'comments': comments,
'comm_num': comments_number}
return context
class AddTopic(LoginRequiredMixin, DataMixin, CreateView):
form_class = AddTopicForm
template_name = 'forum/addtopic.html'
page_title = 'Создание новой темы'
class AddComment(LoginRequiredMixin, DataMixin, CreateView):
form_class = AddCommentForm
template_name = 'forum/addcomment.html'
page_title = 'Оставить комментарий'
success_url = reverse_lazy('topic')
class UpdateComment(LoginRequiredMixin, DataMixin, UpdateView):
form_class = AddCommentForm
template_name = 'forum/addcomment.html'
page_title = 'Редактировать комментарий'
success_url = reverse_lazy('topic')
class UserProfile(DetailView):
model = Profile
template_name = "profile.html"
forms.py:
from django import forms
from django.core.exceptions import ValidationError
#from forum.models import Topic, Comment
class AddTopicForm(forms.ModelForm):
subject = forms.CharField(label="Заголовок", max_length=100, min_length=7)
first_comment = forms.CharField(label="Сообщение", widget=forms.Textarea())
class Meta:
#model = Topic
fields = ['subject', 'first_comment']
def clean_subject(self):
subject = self.cleaned_data['subject']
if len(subject) > 100:
raise ValidationError("Длина превышает 100 символов")
if len(subject) < 7:
raise ValidationError("Слишком короткое заглавие, требуется не менее 7 символов")
return subject
class AddCommentForm(forms.ModelForm):
content = forms.CharField(label="Текст комментария", max_length=2000, min_length=1, widget=forms.Textarea())
class Meta:
#model = Comment
fields = ['content']
Я не уверен, нужно ли это или нет, но также urls.py для вас:
from django.urls import path
from forum.views import *
urlpatterns = [
#path('<slug:profile_slug>/', user_profile, name='user_profile'),
path('', SubForumListView.as_view(), name='forum'),
path('<slug:subforum_slug>/', TopicListView.as_view(), name='subforum'),
path('subforum/<slug:topic_slug>/', ShowTopic.as_view(), name='topic'),
path('subforum/add-topic/', AddTopic.as_view(), name="add_topic"),
path('subforum/<slug:topic_slug>/add-comment/', AddComment.as_view(), name="add_comment"),
path('subforum/<slug:topic_slug>/edit/<int:id>/', UpdateComment.as_view(), name="edit_comment"),
Если необходимы какие-то дополнительные файлы/информация, я готов их предоставить. На данный момент я не могу продолжить реализацию проекта, так как CI не позволяет мне протестировать страницу форума. Поэтому я должен решить эту проблему, прежде чем предпринимать какие-либо дальнейшие действия.
Не импортируйте представления в модели. Представления должны зависеть от моделей, никогда наоборот.
Вы можете использовать имя пути, так:
class Subforum(models.Model):
# …
def get_absolute_url(self):
return reverse(
'subforum',
kwargs={'name': self.title, 'subforum_slug': self.slug},
)
То же самое для ShowTopic
, и, таким образом, удалите импорт.
Note: You can make use of
django-autoslug
[GitHub] to automatically create a slug based on other field(s).