Django circular import (models-views(-forms))

Просмотрел все предыдущие темы, посвященные этой проблеме, но так и не нашел правильного решения, поэтому решил создать свой вопрос.

Я создаю проект форума (как часть проекта сайта). Представления сделаны на основе классов: SubForumListView ведет на главную страницу форума, где перечислены его основные разделы («подфорумы»). TopicListView, в свою очередь, ведет на страницы определенных подфорумов со списком активных тем, созданных в рамках данного подфорума. Вид ShowTopic ведет на страницу определенной темы со списком комментариев.

Проблема проявляется из-за:

  1. Models.py: Метод get_absolute_url в модели Subforum, который в функции reverse секции return принимает вид в качестве 1-го аргумента; я пытался избежать прямого импорта вида, но программа не принимает других вариантов;
  2. Views.py: большинство представлений импортируют модели либо в аргументах экземпляра (model = Subforum), либо в методах, использующих кверисеты (как в get_context_data: topics = Topic.objects.all()); я не могу с уверенностью сказать, помогает ли замена аргумента экземпляра model = Subforum на model = 'Subforum', поскольку это невозможно сделать с методами кверисета и поэтому не может быть доказано;
  3. Forms.py: мои классы форм были созданы с помощью forms.ModelForm и include class 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).

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