简体   繁体   中英

Django form.is_valid() returns False

I am creating a blog in Django and I'm stuck at adding writing post_update_view, where form.is_valid() returns False and I'm not able to update the post.

I've also checked this but I could not navigate to the right spot.

Also, it's weird as for post creation form.is_valid() works fine (I'm assigning " form = PostModelForm(request.POST or None) " there).

I would appreciate any suggestions.

Below is:

Views.py

def post_update_view(request, slug):
    obj = get_object_or_404(Post, slug=slug)
    form = PostModelForm(request.POST or None, instance=obj)
    print(form.is_valid())
    if form.is_valid():
        form.save()
    template_name = 'blog/post_update.html'
    context = {'form': form, 'title': f'Update {obj.title}'}
    return render(request, template_name, context)

models.py:

from django.conf import settings
from django.db import models

User = settings.AUTH_USER_MODEL

class Post(models.Model):
    CATEGORY_CHOICES = (
        (None, '--------'),
        ('CB', 'City Break'),
        ('1W', 'Week holidays'),
        ('2W', 'Two weeks holidays'),
        ('MO', 'Month holidays'),
        ('SB', 'Sabbatical'),
    )

    CONTINENT_CHOICES = (
        (None, '--------'),
        ('AS', 'Asia'),
        ('AU', 'Australia and Oceania'),
        ('AF', 'Africa'),
        ('EU', 'Europe'),
        ('NA', 'North America'),
        ('SA', 'South America'),
    )

    user = models.ForeignKey(
        User,
        default=1,
        null=True,
        on_delete=models.SET_NULL
    )
    title = models.CharField(max_length=150)
    date = models.DateField()
    category = models.CharField(
        max_length=100,
        choices=CATEGORY_CHOICES,
        blank=True
    )
    continent = models.CharField(
        max_length=100,
        choices=CONTINENT_CHOICES,
        blank=True
    )
    body = models.TextField()
    slug = models.SlugField()

    def __str__(self):
        return self.slug

forms.py:

from django import forms
from blog.models import Post


class PostModelForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'date', 'category', 'continent', 'slug', 'body']

    def clean_slug(self):
        instance = self.instance
        slug = self.cleaned_data.get('slug')
        qs = Post.objects.filter(slug__iexact=slug)
        qs_title_id = 0
        if instance is not None:
            qs = qs.exclude(pk=instance.pk)
        if qs.exists():
            raise forms.ValidationError(f"Slug has already been used in "
                                        f"'{qs[qs_title_id]}'.\n Please "
                                        f"change.")
        return slug

templates

{% extends "site_general/header.html" %}

{% block content %}
    {% if title %}
        <h1>{{ title }}</h1>
    {% endif %}

    <form method="POST" action="."> {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Update</button>
    </form>

{% endblock %}

urls

from django.urls import path
from .views import (
    post_delete_view,
    post_detail_view,
    post_list_view,
    post_update_view,
)

urlpatterns = [
    path('', post_list_view),
    path('<str:slug>/', post_detail_view),
    path('<str:slug>/edit', post_update_view),
    path('<str:slug>/delete', post_delete_view),
]

Too long for a comment, and I'm not sure whether this will solve the problem, but bruno desthuillers has linked to "what a view should look like". The important thing to note is that there is one code path for an unbound form (URL accessed via HTTP GET) and a different one for data submitted from the browser by HTTP POST.

I have always detested the way that canonical form sandwiches the processing of validated form data with boilerplate on both sides, and would suggest negating the validity test, and using a little Python magic to combine the form instantiation for both GET and POST cases:

def post_update_view(request, slug):
    obj = get_object_or_404(Post, slug=slug)
    args = [request.POST] if request.method == "POST" else []
    form = PostModelForm(*args, instance=obj)
    if request.method != "POST" or not form.is_valid(): 
        # GET or not valid 
        context = {'form': form, 'title': f'Update {obj.title}'}
        return render(request, 'blog/post_update.html', context)

    # the form validated, do the work and redirect
    form.save()
    return redirect("wherever you want, but redirect")

If this isn't the answer, replace your template with the simplest possible {{ form.as_p }} and a submit button, and see whether that fixes it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM