I am trying to create a django web app for displaying various data (a question and some additional data).My data consists of a question body and industry guidance, which contains several bullet points. Each bullet point may contain additional sub options itself as well, etc.
Example data:
Question body: Was everyone familiar with the location, purpose, testing and oepration of fire doors? Industry Guidance:
Below are my models:
class BaseGuidance(models.model):
text = models.CharField(max_length=2000)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name="sub_options")
class Meta:
abstract = True
def __str__(self):
return self.text
class IndustryGuidance(BaseGuidance):
pass
class Question(models.model):
body = models.CharField(max_length=400)
industry_guidance = models.ForeignKey(IndustryGuidance, on_delete=models.CASCADE, null=True, blank=True)
Here is my view, which is rendering the HTML:
def index(request):
questions = Question.objects.all()
industry_guidance = IndustryGuidance.objects.prefetch_related(
'sub_options',
).all()
return render(request, "sire/index.html",{
"questions": questions,
"industry_guidance": industry_guidance
})
Below is my HTML:
<td scope="col">
<ul class="list-group">
{% for guidance in industry_guidance %}
<li>{{ guidance.text }} </li>
<ul>
{% for sub_option in guidance.sub_options.all %}
<li>{{ sub_option.text }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
</td>
I am really confused on to how exactly I should approach this problem in order to fetch all data and display accordingly.
I tried prefetch_related, but my web app would not load, apparently stuck into some for loop cycle. I am also thinking of rearranging my models but I could not find any guidance or best practices on the matter.
Ultimately, I would like to display each question in a table with its industry guidance related to it a list. Each sub option should be indented into another list. Each bullet of sub option should be in another list, and so on... All data regarding the question should be in the same row.
I'd recommend you using django-mptt when working with trees.Here's an example of usage:
pip install django-mptt
add "mptt" to your INSTALLED_APPS
list in settings.py
INSTALLED APPS = [
...,
"mptt",
]
Modify the parent
field of your BaseGuidance
model in models.py
:
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
class BaseGuidance(MPTTModel):
text = models.CharField(max_length=2000)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name="sub_options")
class Meta:
abstract = True
class MPTTMeta:
order_insertion_by = ['-id']
parent_attr = 'parent'
def __str__(self):
return self.text
After you modify the model, migrate the changes:
python ./manage.py makemigrations
python ./manage.py migrate
Then modify your views:
def index(request):
questions = (
Question.objects.select_related('industry_guidance')
.prefetch_related('industry_guidance__sub_options') # Fetch sub-options for the MPTT structure
)
context = []
for question in questions:
guidance_tree = (
question.industry_guidance.get_descendants(include_self=True)
if question.industry_guidance else None
)
context.append({
"question": question,
"guidance_tree": guidance_tree,
})
return render(request, "index.html", {"context": context})
And your sire/index.html
file:
{% for item in context %}
<tr>
<td>{{ item.question.body }}</td>
<td>
<ul>
{% if item.guidance_tree %}
{% recursetree item.guidance_tree %}
<li>{{ node.text }}</li>
<ul>
{% for child in node.get_children %}
<li>{{ child.text }}</li>
{% endfor %}
</ul>
{% endrecursetree %}
{% else %}
<li>No guidance available for this question.</li>
{% endif %}
</ul>
</td>
</tr>
{% endfor %}
Hope this works for you!