Reputation: 3779
In a blog-ish part of my django app, editing an existing entry results in creating a new entry. It should modify the existing entry.
I have a simple model:
class BlogEntry(models.Model):
...
slug = models.SlugField(unique=True)
and a simple form for editing it:
class BlogEntryForm(forms.ModelForm):
...
slug = forms.CharField(required=False, widget=forms.HiddenInput())
class Meta:
model = BlogEntry
fields = (..., 'slug')
and the view, somewhat simplified, is also straight-forward:
class BlogEditView(View):
@method_decorator(login_required)
def get(self, request, slug=None):
context = {
'user': request.user,
}
if slug is None:
print('Creating new entry.')
context['entry'] = BlogEntryForm()
context['entry'].publication_date = datetime.datetime.now()
return render(request, 'blog/edit.html', context)
print('Using existing entry.')
entry = get_object_or_404(BlogEntry, slug=slug)
context['entry'] = BlogEntryForm(instance=entry)
return render(request, 'blog/edit.html', context)
@method_decorator(login_required)
def post(self, request):
blog_entry_form = BlogEntryForm(request.POST)
if blog_entry_form.is_valid():
blog_entry = blog_entry_form.save(commit=False)
if blog_entry.slug is None or blog_entry.slug == '':
print('New entry, slug is empty.')
blog_entry.creation_date = datetime.datetime.now()
blog_entry.slug = slugify(blog_entry.title) + '_' + hex(random.randint(0, 1e10))
blog_entry.author = request.user
blog_entry.save()
return redirect(reverse('blog/article', args=[blog_entry.slug]))
...
In get()
, I confirm with the print's that I am taking the correct branch. If the entry exists, I set entry with the instance. In post()
, however, I always take the new entry branch.
The layout has the typical hidden element loop.
{% for hidden in form.hidden_fields %}
{{ hidden }} hidden
{% endfor %}
though, suspiciously, when I look at the served html, I don't see the slug entry, so it's no surprise that it's not getting passed through.
Anyone see what I'm missing?
Upvotes: 0
Views: 1352
Reputation: 45575
You should add the slug
argument to the post()
method as well. This will allow you to get the blog entry to edit from the database and pass this entry to the form as an instance
argument:
@method_decorator(login_required)
def post(self, request, slug=None):
blog_entry = BlogEntry.objects.filter(slug=slug).first()
blog_entry_form = BlogEntryForm(request.POST, instance=blog_entry)
...
UPDATE: To pass the slug
argument to the post()
method you should use the empty action
attribute of the <form>
tag:
<form action="" method="POST">
In this case the form will be submitted to the url from which it was loaded. So the slug
argument for the POST request will be the same as for the GET.
Upvotes: 2