Home > Software engineering >  Reverse for 'edit_blog_post' with arguments '('',)' not found
Reverse for 'edit_blog_post' with arguments '('',)' not found

Time:06-15

I am trying to create a way to edit individual blog posts from their individual html. Here are the relevant files and trace back. I am somewhat understanding that the issue lies in blog_post.id being due to the fact that blog_post has not carried over from the for loop on blog_posts.html. I have read up on others having this issue and they all structured their pages to have the edit button being inside the original for loop, which makes sense in hindsight. BUT now that I have run into this issue, I'm determined to understand how I can solve it without going back and restructuring my pages to align with the others I saw.

urls.py


from django.urls import path

from . import views

app_name = 'blogs'
urlpatterns = [
    # Home page
    path('', views.index, name='index'),
    path('blog_posts/', views.blog_posts, name='blog_posts'),
    path('blog_posts/<int:blog_post_id>/', views.blog_post, name='blog_post'),
    path('new_blog_post/', views.new_blog_post, name='new_blog_post'),
    path('edit_blog_post/<int:blog_post_id>/', views.edit_blog_post, name='edit_blog_post'),

]

views.py


from .models import BlogPost
from .forms import BlogPostForm

def index(request):
    """Home page for Blog."""
    return render(request, 'blogs/index.html')

def blog_posts(request):
    """Show all Blog Posts."""
    blog_posts = BlogPost.objects.order_by('date_added')
    context = {'blog_posts': blog_posts}
    return render(request, 'blogs/blog_posts.html', context)

def blog_post(request, blog_post_id):
    """Show details of an individual blog post."""
    blog_post = BlogPost.objects.get(id=blog_post_id)
    title = blog_post.title
    id = blog_post_id
    date = blog_post.date_added
    text = blog_post.text
    context = {'title': title, 'text': text, 'date': date}
    return render(request, 'blogs/blog_post.html', context)

def new_blog_post(request):
    """Add a new blog post"""
    if request.method != 'POST':
        # No data submitted, create a blank form.
        form = BlogPostForm()
    else:
        # POST data submitted, process data.
        form = BlogPostForm(data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('blogs:blog_posts')
    # Display a blank or invalid form.
    context = {'form': form}
    return render(request, 'blogs/new_blog_post.html', context)

def edit_blog_post(request, blog_post_id):
    """Edit an existing blog post's title or text."""
    blog_post = BlogPost.objects.get(id=blog_post_id)
    if request.method != 'POST':
        # Initial request, prefill with the current data.
        form = BlogPostForm(instance=blog_post)
    else:
        # POST data submitted; process new data.
        form = BlogPostForm(instance=blog_post, data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('blogs:blog_post', blog_post_id=blog_post.id)
    context = {'blog_post': blog_post, 'form': form}
    return render(request, 'blogs/edit_blog_post.html',  context)

models.py

from django.db import models

class BlogPost(models.Model):
    """A post the user is posting on their blog."""
    title = models.CharField(max_length=200)
    text = models.TextField()
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """Return a string representation of the model"""
        return f"{self.title.title()}"

blog_posts.html

{% extends 'blogs/base.html' %}

{% block content %}

<p>Blog Posts</p>

<ul>
    {% for blog_post in blog_posts %}
    <li>
        <a href="{% url 'blogs:blog_post' blog_post.id %}">{{ blog_post }}</a>
    </li>
    {% empty %}
    <li>No posts have been made yet.</li>
    {% endfor %}
</ul>

<a href="{% url 'blogs:new_blog_post' %}">Add a new blog post</a>

{% endblock content %}

blog_post.html

{% extends 'blogs/base.html' %}

{% block content %}

<p>Blog Post: {{ title }}</p>

<p>Entry:</p>

<p>{{ text }}</p>
<p>{{ date }}</p>

<p>
    <a href="{% url 'blogs:edit_blog_post' blog_post.id %}">Edit Blog Post</a>
</p>

{% endblock content %}

edit_blog_post.html

{% extends "blogs/base.html" %}

{% block content %}

<p>
    <a href="{% 'blogs:blog_post' blog_post.id %}">{{ blog_post }}</a>
</p>
    <p>Edit Blog Post</p>

<form action="{% url 'blogs:edit_blog_post' blog_post.id %}" method='post'>
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">Save Changes</button>
</form>

{% endblock content %}

Reverse for 'edit_blog_post' with arguments '('',)' not found. 1 pattern(s) tried: ['edit_blog_post/(?P<blog_post_id>[0-9] )/\Z']

3   {% block content %}
4   
5   <p>Blog Post: {{ title }}</p>
6   
7   <p>Entry:</p>
8   
9   <p>{{ text }}</p>
10  <p>{{ date }}</p>
11  
12  <p>
13      <a href="{% url 'blogs:edit_blog_post' blog_post.id %}">Edit Blog Post</a>
14  </p>
15  
16  {% endblock content %}

CodePudding user response:

If I've read the question correctly, You're getting the error becuase you are not providing the necessary ID to the URL construction part of your template.

You're separating out the elements (date, content etc) to send to the template, but not passing the ID at the same time. You could send the ID in as a separate context variable, but that's extra typing for no real reward.

It's easiest to pass in the post itself via context and refer to its attributes in the template - I think it makes it easier to read also. That way the ID is there when you need to contruct the edit link, and if you change the model to possess extra fields, you don't need to convert and add to the context as the whole post is already there.

views.py

def blog_post(request, blog_post_id):
    """Show details of an individual blog post."""
    blog_post = BlogPost.objects.get(id=blog_post_id) #this is all we need
    context = {"blog_post_context": blog_post}

    return render(request, 'blogs/blog_post.html', context)

blog_post.html

{% extends 'blogs/base.html' %}

{% block content %}

<p>Blog Post: {{ blog_post_context.title }}</p>

<p>Entry:</p>

<p>{{ blog_post_context.text }}</p>
<p>{{ blog_post_context.date }}</p>

<p>
    <a href="{% url 'blogs:edit_blog_post' blog_post_context.pk %}">Edit Blog Post</a>
</p>

{% endblock content %}

If that all works, look into using get_object_or_404 rather than Post.objects.get for some additional robustness.

CodePudding user response:

I assume you got the error when you try visiting the blog_post.html page. If I'm correct, then here's an approach you could take...

In your views.py

def blog_post(request, blog_post_id):
     """Show details of an individual blog post."""
     # blog_post = BlogPost.objects.get(id=blog_post_id)
     blog_post = get_object_or_404(id=blog_post_id)  # Recommended

     # Commented lines below are somewhat not necessary...
     
     # title = blog_post.title
     # id = blog_post_id
     # date = blog_post.date_added
     # text = blog_post.text

     context = {'blog_post': blog_post}

     return render(request, 'blogs/blog_post.html', context)

edit_blog_post.html is expecting an object called blog_post to be able to access the blog_post.id for {% url 'blogs:edit_blog_post' blog_post.id %}.

Now within the edit_blog_post.html file.

{% block content %}
     <p>Blog Post: {{ blog_post.title }}</p>

     <p>Entry:</p>
     <p>{{ blog_post.text }}</p>
     <p>{{ blog_post.date_added }}</p>

     <p>
          <a href="{% url 'blogs:edit_blog_post' blog_post.id %}">Edit Blog Post</a>
     </p>
{% endblock content %}
  • Related