Invalid form submission not working

I am working on a blog app that is pretty much finished, but I have a problem with submission of an invalid form.
If I use hx-post=“{{ request.path }}” for the form submission it works as expected, but when I try to use named url (which is the recommended approach from what I’ve seen) I get the following server error when the form is invalid:
django.urls.exceptions.NoReverseMatch: Reverse for ‘blog-post-detail’ with arguments ‘(’‘,)’ not found. 1 pattern(s) tried: [‘blog/(?P<url_identifier>[-a-zA-Z0-9_]+)\Z’]
[12/Jun/2024 13:03:44] “POST /blog/petrlaska-pecina HTTP/1.1” 500 151029

Everything works well if the form is valid. What am I doing wrong? Should I keep using “request.path” for the form action, as it was not causing any problems?

This is my form snippet:

<form hx-post="{% url 'blog:blog-post-detail' blogpost.url_identifier %}"
      hx-target="#comment-section"
      hx-swap="innerHTML"
      id="comment-form"
      novalidate
>
    {% csrf_token %}

    {% if messages %}
        {% for message in messages %}
            <p id="msg">{{ message }}</p>
        {% endfor %}
    {% endif %}

    {% if comments %}
        <h3>Komentari ({{ comments.count }})</h3>
    {% else %}
        <h3>Komentari (0)</h3>
    {% endif %}

    {% if user.is_authenticated %}
        {{ comment_form.content }}
        {% if comment_form.content.errors %}
            {% for error in comment_form.content.errors %}
            	<p class="error-msg">{{ error }}</p>
            {% endfor %}
        {% endif %}

    {% else %}
        {{ comment_form.content }}
        {% if comment_form.content.errors %}
            {% for error in comment_form.content.errors %}
            	<p class="error-msg">{{ error }}</p>
            {% endfor %}
        {% endif %}

        <p class="input-field">
            {{ comment_form.name }}
        </p>
        {% if comment_form.name.errors %}
            {% for error in comment_form.name.errors %}
            	<p class="error-msg">{{ error }}</p>
            {% endfor %}
        {% endif %}

        <p class="input-field">
            {{ comment_form.email }}
        </p>
        {% if comment_form.email.errors %}
            {% for error in comment_form.email.errors %}
                <p class="error-msg">{{ error }}</p>
            {% endfor %}
        {% endif %}
    {% endif %}

    <button type="submit">Pošalji</button>
</form>

This is the view that handles it:

class BlogPostDetailView(DetailView):
    model = BlogPost
    template_name = "blog/blog_detail.html"

    # Setting the path parameter
    slug_field = 'url_identifier'
    slug_url_kwarg = 'url_identifier'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['comments'] = Comment.objects.filter(blog_post=self.object).order_by('-created_on')

        # Initialize "name" and "email" fields based on the cookies
        name = self.request.COOKIES.get('blog_comment_name', '')
        email = self.request.COOKIES.get('blog_comment_email', '')

        if not self.request.user.is_authenticated:
            # Checking if post has already been liked by the reader
            liked_posts = self.request.COOKIES.get('liked_blog_posts', '').split(',')
            context['liked'] = str(self.object.pk) in liked_posts

            # Setting the reader's name and email based on whether they have already commented on the post
            context['comment_form'] = CommentForm(initial={'name': name, 'email': email})
        else:
            context['comment_form'] = CommentForm(user=self.request.user)

        # oembed to iframe conversion for CKeditor5
        context['content'] = transform_oembed(self.object.content)

        return context

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()

        # Increment the view count
        self.object.views += 1
        self.object.save(update_fields=['views'])

        context = self.get_context_data(object=self.object)
        return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = CommentForm(request.POST, user=self.request.user)

        if form.is_valid():
            comment = form.save(commit=False)

            # Set default values for admin users
            if self.request.user.is_authenticated:
                comment.author = self.request.user
                comment.name = self.request.user.first_name + ' ' + self.request.user.last_name
                comment.email = self.request.user.email

            comment.blog_post = self.object
            comment.save()

            comments = Comment.objects.filter(blog_post=self.object).order_by('-created_on')
            comment_form = CommentForm(user=self.request.user)

            # Set predefined comment form values for name and email for non-authenticated readers if stored in cookies
            if not self.request.user.is_authenticated:
                name = self.request.COOKIES.get('blog_comment_name', '')
                email = self.request.COOKIES.get('blog_comment_email', '')
                comment_form = CommentForm(initial={'name': name, 'email': email})

            # messages.success(request, 'Komentar uspešno dodat')
            ctx = self.get_context_data()
            ctx.update({'comments': comments, 'comment_form': comment_form})
            response = render(request, 'blog/partials/comment_section.html', ctx)

            # Set name and email cookie values for readers who comment for the first time
            if not self.request.user.is_authenticated:
                if 'blog_comment_name' not in request.COOKIES:
                    response.set_cookie('blog_comment_name', request.POST['name'], max_age=157_784_760)
                if 'blog_comment_email' not in request.COOKIES:
                    response.set_cookie('blog_comment_email', request.POST['email'], max_age=157_784_760)

            return response

        else:
            comments = Comment.objects.filter(blog_post=self.object).order_by('-created_on')
            ctx = {'comments': comments, 'comment_form': form}
            return render(request, 'blog/partials/comment_section.html', ctx)

project level urls:

urlpatterns = [
    path("admin/", admin.site.urls),
    path("blog/", include("blog.urls")),
    path("", views.return_to_blog),
]

app level urls:

app_name = 'blog'
urlpatterns = [
    path("", views.BlogPostListView.as_view(), name="blog-post-list"),
    path("<slug:url_identifier>", views.BlogPostDetailView.as_view(), name="blog-post-detail"),
]

What does your BlogPost model look like? Have you verified that you have a value in the url_identifier field of the BlogPost instance being rendered?

Here is the model:

class BlogPost(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
    cover_image = models.ImageField(upload_to="cover_img/", null=True, blank=True)
    content = CKEditor5Field("Text", config_name="extends")
    snippet = models.TextField(default='')
    created_on = models.DateTimeField(auto_now_add=True)
    last_modified = models.DateTimeField(auto_now=True)
    views = models.IntegerField(default=0, validators=[MinValueValidator(0)])
    likes = models.IntegerField(default=0, validators=[MinValueValidator(0)])
    url_identifier = models.SlugField(max_length=255, unique=True, blank=True)

    def __str__(self):
        return f"{self.title}"

When I try to print the value of the url_identifier from the view when I submit invalid form, it prints it correctly. I returned the action to request.path, it works in my case. But ChatGPT says it’s not the best practice so I’m a little bit confused. Obviously I’m still learning the basics…

This is the else clause when the form is not valid. Notice that you are not supplying a blogpost instance in the context for this.

Amazing! Thank you so much! It works now.