为什么内联表单集不验证

问题描述

我有两个具有外键关系的模型

models.py

from django.db import models

# Create your models here.
class Project(models.Model):
    STATUSES = (
        ('Ongoing','Ongoing'),('Completed','Completed')
    )

    YEARS = (
        (2019,2019),(2020,2020),(2021,2021),(2022,2022)
    )
    
    name = models.CharField(max_length=200)
    client = models.CharField(max_length=100)
    year = models.SmallIntegerField(choices=YEARS)
    status = models.CharField(max_length=10,choices=STATUSES)
    picture = models.ImageField(blank=True,null=True)
    description = models.TextField(blank=True,null=True)

    def __str__(self):
        return self.name


class Photo(models.Model):
    project = models.ForeignKey("Project",on_delete=models.CASCADE,related_name="images",blank=True,null=True)
    image = models.ImageField()
    description = models.CharField(max_length=100,null=True)
    slide = models.BooleanField(default=False)

我希望在同一个表单上创建照片和项目,所以我使用了 inline_formset_factory

forms.py

from django.forms import inlineformset_factory
from projects.models import Photo,Project
from django.forms import ModelForm


class ProjectModelForm(ModelForm):
    class Meta:
        model = Project
        fields = (
            'name','client','year','picture','status','description',)


class PhotoModelForm(ModelForm):
    class Meta:
        model = Photo
        fields = (
            'image','slide','description'
        )


PhotoFormset = inlineformset_factory(Project,Photo,form=PhotoModelForm,extra=1)

我使用了通用的 CreateView

views.py

from django.contrib.auth.mixins import LoginrequiredMixin
from projects.forms import PhotoFormset,ProjectModelForm
from django.shortcuts import redirect,reverse,render
from django.views.generic import ListView,DetailView,CreateView,UpdateView,DeleteView
from .models import Photo,Project

# Create your views here.
class ProjectCreateView(LoginrequiredMixin,CreateView):
    template_name = 'projects/project_create.html'
    form_class = ProjectModelForm

    def get_context_data(self,**kwargs):
        context = super(ProjectCreateView,self).get_context_data(**kwargs)
        context['photos_formset'] = PhotoFormset()
        return context
    
    def post(self,request,*args,**kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        photo_formset = PhotoFormset(self.request.POST)
        if form.is_valid() and photo_formset.is_valid():
            return self.form_valid(form,photo_formset)
        else:
            return self.form_invalid(form,photo_formset)
    
    def form_valid(self,form,photo_formset):
        self.object = form.save(commit=False)
        self.object.save()
        print('valid')
        photos = photo_formset.save(commit=False)
        for photo in photos:
            photo.project = self.object
            photo.save()
        return reverse('projects:projectspage')

    def form_invalid(self,photo_formset):
        if not photo_formset.is_valid():
            print('invalid formset')
        return self.render_to_response(
            self.get_context_data(form=form,photos_formset=photo_formset)
        )

    def get_success_url(self):
        return reverse('projects:projectspage')

这是模板

{% extends 'base.html' %}
{% load crispy_forms_filters %}
<!-- crispy_forms_tags -->

{% block content %}
    <div class="container">
        <h2>create new project</h2>
        <form method="post">
            {% csrf_token %}
            {{ form|crispy }}
            <h2 class="display-6 my-5">
                Add photos
            </h2>
            {{ photos_formset.as_p }}
            <input type="submit" value="Create" class="btn btn-primary">
        </form>
    </div>
{% endblock content %}

当我提交时,视图的post方法返回了form_invalid

我如何让 inline_formset 进行验证或者有更好的方法来做到这一点

解决方法

我更喜欢把这些视图写成函数,看起来更直接,试试:

@login_required
def post_news(request):
    if request.method == 'POST':
        project_form = ProjectModelForm(request.POST,request.FILES)
        photo_form = PhotoModelForm(request.POST,request.FILES)
        picture = request.FILES['picture']
        image = request.FILES['image']
        if project_form.is_valid() and photo_form.is_valid():
            project_instance = project_form.save(commit=False)
            project_instance.picture = picture
            project_instance.save()
            photo_instance = project_form.save(commit=False)
            photo_instance.project = project_instance
            photo_instance.image = image
            photo_instance.save()                

            return reverse('projects:projectspage')
else:
    project_form = ProjectModelForm()
    photo_form = PhotoModelForm()

return render(request,'projects/project_create.html',{'project_form': project_form,'photo_form': photo_form})

我认为您必须考虑没有提供项目模型图片的情况。只需将两个表单都弹出到您的模板中,这应该可以工作。