Django(2.0.8)通用视图中的嵌入式表单集工厂

时间:2018-10-27 05:41:59

标签: django django-forms

我试图创建一个内联表单集工厂,以便在我同时创建Post时,我可以创建一个与ForeignKey关联的PostVocab。我看了几本教程,但在这个问题上陷入了困境。保存时向我显示: ValueError: save() prohibited to prevent data loss due to unsaved related object 'post'.

My Models:

from django.db import models
from PIL import Image
from django.urls import reverse
from ckeditor.fields import RichTextField
from django.db.models.signals import pre_save
from django.utils.text import slugify

from django.conf import settings
# Create your models here.

def upload_location(instance, filename):
    filename = instance.title
    return "blog/%s.jpg" %(filename)

class Post(models.Model):
    title       = models.CharField(max_length=100)
    slug        = models.SlugField(unique=True)
    pdf         = models.FileField(blank=True, null=True)
    audio_file  = models.FileField(blank=True, null=True)
    author      = models.CharField(max_length=20)
    added       = models.DateTimeField(auto_now=False,auto_now_add=True)
    updated     = models.DateTimeField(auto_now_add=False, auto_now=True)
    draft       = models.BooleanField(default=False)
    publish     = models.DateField(auto_now=False, auto_now_add=False)
    post        = RichTextField(blank=True, null=True)
    picture     = models.ImageField(upload_to=upload_location, blank=True, null=True)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail' , kwargs={'slug':self.slug})

    def save(self, **kwargs):
        if self.picture:
            super(Post, self).save()
            mywidth = 440
            image = Image.open(self.picture)
            wpercent = (mywidth / float(image.size[0]))
            hsize = int((float(image.size[1]) * float(wpercent)))
            image = image.resize((mywidth, hsize), Image.ANTIALIAS)
            image.save(self.picture.path)

from django.db import models
from blog.models import Post
# Create your models here.
class PostVocab(models.Model):
    post        = models.ForeignKey(Post, on_delete=models.CASCADE)
    word        = models.CharField(max_length = 30)
    explanation = models.CharField(max_length = 200)
    example     = models.CharField(max_length = 100)

    def __str__(self):
        return self.word

我的表单:

from django import forms
from ckeditor.widgets import CKEditorWidget
from vocabulary.models import PostVocab
from .models import Post
from django.forms.models import inlineformset_factory

class PostForm(forms.ModelForm):

    post = forms.CharField(widget=CKEditorWidget())

    class Meta:
        model  = Post
        fields = ['title', 'author','picture','post','draft','publish']



PostVocabFormSet = inlineformset_factory(
    Post,
    PostVocab,
    fields=['word','explanation','example'],
    extra=1,
    can_delete=True
)

我的观点:

class CreatePost(View):
    form      = PostForm
    formset   = PostVocabFormSet
    template_name = 'blog/post_form.html'
    def get(self, request):
        if not request.user.is_staff or not request.user.is_superuser:
            raise Http404
        return render(request,self.template_name, {"form":self.form,'formset':self.formset})
    def post(self, request):
        if not request.user.is_staff or not request.user.is_superuser:
            raise Http404
        form = self.form(request.POST, request.FILES)
        formset = self.formset(request.POST, request.FILES)
        print(form.errors)
        if form.is_valid() and formset.is_valid():
            post = form.save(commit=False)
            post.save()
            words = formset.save(commit=False)
            for word in words:
                word.post = post
                word.save()
            return redirect('blog:main')
        return render (request, self.template_name,{"form":self.form,'formset':self.formset})

我的模板:

{% extends 'main/base.html' %}
{% block content %}

<div class="row">
    <div class="col-sm-12 col-md-10 col-md-offset-1 col-lg-10 col-lg-offset-1">
        <div class="panel panel-default">
            <div class="panel-body">
                <form class="form-horizontal" method="post" action="" enctype="multipart/form-data">
                    {% csrf_token %}
                    {{ form.as_p }}
                    {{ formset.management_form }}
                    <table role="grid" class="stack hover" style="width:100%">
                        <thead>
                            <tr>
                                <th scope="col" class="text-center" style="width=10%">Order</th>
                                <th scope="col" class="text-center" style="width=10%">Word</th>
                                <th scope="col" class="text-center" style="width=10%">Explanation</th>
                                <th scope="col" class="text-center" style="width=10%">Example</th>
                                <th scope="col" class="text-center" style="width=10%">Delete</th>
                            </tr>
                        </thead>
                        <tbody class="order">
                            {% for form in formset %}
                                <tr class="postvocab-form">
                                    <td>{{ form.id }}</td>
                                    <td>{{ form.word }}</td>
                                    <td>{{ form.explanation }}</td>
                                    <td>{{ form.example }}</td>
                                    {% if form.instance.pk %}<td class="text-center">{{ form.DELETE }}</td>
                                    {% else %}<td class="text-center"></td>
                                    {% endif %}
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <button type="submit" class="btn btn-primary" >Submit</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

我认为这是

if form.is_valid() and formset.is_valid():
            post = form.save(commit=False)
            post.save()
            words = formset.save(commit=False)
            for word in words:
                word.post = post
                word.save()
            return redirect('blog:main')

实际上可以通过将PostVocab实例与Post关联来解决此问题。你们知道此代码有什么问题吗?


更新


更改此位:

if form.is_valid() and formset.is_valid():
        form.save()
        words = formset.save(commit=False)
        for word in words:
            word.post = form
            word.save()
            return redirect('blog:main')

现在我得到了Cannot assign "": "PostVocab.post" must be a "Post" instance.

postVocab.post是“ Post”的实例,因为它具有与之关联的ForeignKey。为什么显示此错误?是因为PostVocab类位于不同的应用程序中(词汇)吗?


UDPATE(在Post模型中具有更改的保存方法)

def save(self, **kwargs):
    super(Post, self).save()
    if self.picture:
        mywidth = 440
        image = Image.open(self.picture)
        wpercent = (mywidth / float(image.size[0]))
        hsize = int((float(image.size[1]) * float(wpercent)))
        image = image.resize((mywidth, hsize), Image.ANTIALIAS)
        image.save(self.picture.path)

仍然得到错误:Cannot assign "": "PostVocab.post" must be a "Post" instance


UDPATE(视图更改-def post())

@UrošTrstenjak @ art06当我打印formset.errors时,我得到了[{'post': ['This field is required.']}]。所以我的代码实际上没有传递if语句if form.is_valid() and formset.is_valid():

我尝试通过以下方式将Formset中的外键关联到Post:

def post(self, request):
    if not request.user.is_staff or not request.user.is_superuser:
        raise Http404
    form = self.form(request.POST, request.FILES)
    formset = PostVocabInlineFormSet(request.POST)
    post_title = form['title'].value()
    print(post_title)
    for f in formset:
        f['post'] = str(post_title)
        print(f['post'])
    print(form.errors)
    print(formset.errors)
    if form.is_valid() and formset.is_valid():
        self.object = form.save()
        formset.instance=self.object
        formset.save()
        return redirect('blog:main')
    return render (request,self.template_name,{"form":self.form,'formset':self.formset})

但我在此位出现错误: f['post'] = str(post_title)

错误为'PostVocabForm' object does not support item assignment

1 个答案:

答案 0 :(得分:0)

您已经覆盖了Post模型上的save方法,但是仅在self.picture不是None的情况下保存了Post。首先,您应该将super(Post, self).save()放在if语句之前,然后处理图片。

您还应该将post字段添加到PostVocabFormSet

PostVocabFormSet = inlineformset_factory(
    Post,
    PostVocab,
    fields=['post', 'word', 'explanation', 'example'],
    extra=1,
    can_delete=True
)