Laurie Crean
Laurie Crean

Reputation: 99

Tag instances via django-taggit are not rendering as expected in update post form

{{ form.tags | as_crispy_field }} is not rendering as expected when retrieving the question instance. The same crispy forms syntax works and renders as perfectly when creating an initial post

Pressing Submit leads to Server 500 error. Testing results are documented inline:

<!-- templates/update_question.html -->
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}

{% block extra_head %}
<!-- django-quill-editor Media -->
{% include 'django_quill/media.html' %}
{% endblock %}

{% block content %}

<main class="flex-shrink-0 main-bg">
    <div class="container-question my-5">
[...]
        <form id="standards-form" method="post" action="{% url 'question_update' slug=question.slug %}">
            {% csrf_token %}
        [...]
            
            <h2 class="text-center">Subject line</h2>
            {{ form.subject | as_crispy_field }} <!-- rendering as expected-->

            
            <br>
            <h2 class="text-center">Details</h2>
            {{ form.content | as_crispy_field }} <!-- rendering as expected-->
            <br>
            <h2 class="text-center">Tags</h2>
            <div class="tag-input">
                <div class="input">
                    <div>
                        <i class="fa fa-tags"></i>
                        {{ form.tags | as_crispy_field }} <!--not rendering as expected: reading as [<Tag: tag1>, <Tag: tag2>, <Tag: tag3>]. Expecting tag1 tag2 tag3 to be retrieved from django-taggit's TaggableManager-->
                    </div>
                </div>
                <div class="tag-list mb-4"></div>
            </div>
            <button type="submit" class="btn btn-primary " id="question-submit">Submit</button>
        </form>
        
    </div>

</main>

{% endblock %}

debugging steps

the form is handled in this class I have tried print logs and moving the init function to the top.

# main_forum/forms/question_form.py

# This file contains the form for the Question model. The QuestionForm class will be used to create and update questions in the forum.

from django import forms
from django.contrib.auth.models import User
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from ..models import Question, Answer
from django_quill.forms import QuillFormField
from taggit.forms import TagField
from django.contrib.auth.forms import PasswordChangeForm


class QuestionForm(forms.ModelForm):
    [...]
    tags = forms.CharField(required=False)

    class Meta:
        model = Question  # Specifies the model in models.py associated with this form
        fields = ['subject', 'content', 'tags']

    def __init__(self, *args, **kwargs):
        """
        This is for checking if the form is bound to an existing instance, i.e. if the form is being used to update an existing question.
        """
        super(QuestionForm, self).__init__(*args, **kwargs)
        [...]

        if self.instance.pk: 
            self.fields['tags'].initial = ' '.join(tag.name for tag in self.instance.tags.all()) # Pre-populate the tags field with the existing tags
            print('tags initial:', self.fields['tags'].initial) # PASS. prints as  tags initial: tag1 tag2 tag3

    [...]

    def clean_tags(self):
        tags = self.cleaned_data.get('tags', '')
        return tags

    def save(self, *args, **kwargs):
        instance = super(QuestionForm, self).save(commit=False)
        instance.save()

        # Handling tags here
        tags = self.cleaned_data.get('tags', '')
        tag_names = tags.split()
        print('tag_names after tag.split', tag_names) # expecting ['tag1', 'tag2', 'tag3'] in order to be saved correctly etc. PASS
        instance.tags.clear()

        for tag_name in tag_names:
            instance.tags.add(tag_name.strip())  # Ensure tag is stripped of extra whitespace

        print('instance.tags.all:', instance.tags.all()) # PASS <QuerySet [<Tag: tag1>, <Tag: tag2>, <Tag: tag3>]> etc.

        if self.instance.pk:
            # If this is an update, save the instance again
            instance.save()  # Save the instance again to save the many-to-many relationships
            print('instance saved:', instance) # testing: leads to server 500 error

        return instance



Upvotes: 0

Views: 30

Answers (1)

Laurie Crean
Laurie Crean

Reputation: 99

It appears that the best fix I can find is replacing {{ form.tags | as_crispy_field }} with a custom input that uses {{ question.tags.all|join:' ' }}:

<input type="text" name="tags" id="id_tags id_tags_update" value="{{ question.tags.all|join:' ' }}" /> <!-- this correctly renders the tags as tag1 tag2 tag3 etc.-->

Upvotes: 0

Related Questions