1

I have a hierarchy within my Django App. I want to achieve the following:

  1. First show several boards
  2. Then, if the user clicks on one board, I want to display all topics within the board.

Now the problem is that I have declared a foreign key within the Topic model, but now the topic can be called from each of the boards, not only from within the one I assigned it to.

Here my models:

from django.db import models
from django.contrib.auth.models import User
from mptt.models import TreeForeignKey

# overview over all topics
class Board(models.Model):
    name = models.CharField(max_length=30, unique=True)
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name

# topics withing the boards
class Topic(models.Model):
    board = TreeForeignKey(Board, on_delete=models.CASCADE, related_name='Topic')
    subject = models.CharField(max_length=255, unique=True)
    last_updated = models.DateTimeField(auto_now_add=True) # auto_now_add sets current date+time when created
    starter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='Topic')

    def __str__(self):
        return self.subject

Then the views:

from .models import Board, Topic

class BoardsView(generic.ListView):
    template_name = 'dbconnect/board.html'
    context_object_name = 'board_list'

    def get_queryset(self):
        """ Return all boards."""
        return Board.objects.all()


class TopicView(generic.ListView):
    model = Topic
    template_name = 'dbconnect/topic.html'
    context_object_name = 'topic_list'

    def get_queryset(self):
        """Return all topics."""
        return Topic.objects.all()

The board.html works fine, since there is no second level in the url:

<div style = "text-align: left; margin-left: 10%; margin-right: 10%">
<div style = "display: inline-block; text-align: left;">
    {% if board_list %}
        <ul>
        {% for board in board_list %}
            <li><a href="{% url 'topic' board.id %}">{{ board.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No boards are available.</p>
    {% endif %}
</div>

But then comes topic.html and now I'm lost, what to pass, so that the topic refers to the board:

<div style = "text-align: left; margin-left: 10%; margin-right: 10%">
<div style = "display: inline-block; text-align: left;">
    {% if topic_list %}
        <ul>
        {% for topic in topic_list %}
            <li><a href="{% url 'level' ***HERE*** topic.id %}">{{ topic.subject }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No topics are available.</p>
    {% endif %}
</div>

Here you can see my urls:

app_name = 'dbconnect'
urlpatterns = [
    path('', views.BoardsView.as_view(), name = 'board'),
    path('<int:topic>/', views.TopicView.as_view(), name='topic'),
    path('<int:topic>/<int:level>/', views.LevelView.as_view(), name='level')]

How could I achieve that to show the restricted topic list inside each board?

Ralf
  • 16,086
  • 4
  • 44
  • 68
Nin4ikP
  • 135
  • 1
  • 13

1 Answers1

1

The URLs

If I understand your question correctly, you should probably adapt your URLs to fit the case you describe:

urlpatterns = [
    ...
    path('<int:board_id>/', views.TopicView.as_view(), name='topic-list'),
    ...
]

The first URL can stay the same, but the second URL gets the board_id as URL parameter, not the topic_id, since you want to display a list of topics belonging to the selected board.

And maybe rename that URL from topic to topic-list or something similar to be clearer what it is about.


Now, filtering inside the view:

You can filter your topic queryset like this:

class TopicView(generic.ListView):
    model = Topic
    ...

    def get_queryset(self):
        return super().get_queryset().filter(board=self.kwargs['board_id'])

You can use super().get_queryset() there as you already defined model = Topic on the class; see the docs.

See this post for a similar case.

Ralf
  • 16,086
  • 4
  • 44
  • 68