我试图创建一个内联表单集工厂,以便在我同时创建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类位于不同的应用程序中(词汇)吗?
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
@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
答案 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
)