在 Django inlineformset_factory 中更改图像将其放在列表的末尾
Changing an image in Django inlineformset_factory puts it to the end of the list
假设我正在制作一个 "How to" Django webapp,用户可以在其中制作 post关于如何-做不同的事情。
- "How to"做个绳子
- "How to"做个土锅
- "How to"学骑自行车
你懂的。当成员使post.They向post
添加额外的图像时,我已经使post为this.Now创建视图
例子: "How to"做一根绳子
- 这有Post标题=如何制作绳子
- Post 描述="Some description"
- Post 图片 = 主图
现在他们必须一步一步地展示绳子是如何制作的
- 图 1:第 1 个
- 图 2:第二次这样做
我使用 Django 表单集和我的 post 模型来实现这一点。在创建视图中一切都工作得很好。没问题。但在更新视图中,事情发生了变化。
The Problem
问题是当用户想要编辑他们的 post 并将图像编号 2 从他们的 post 切换到不同的图像时。即使他们改变了第二张图片。该图像现在位于列表的最后。使用户 re-upload 所有图像。带回订单。让我的应用看起来有问题。
示例: 假设用户具有以下 post
main post Title
" Some description "
Main Image = Post_image.jpg
1st Image = A.jpg
Image Title
Image description
2nd Image = B.jpg
Image Title
Image description
3rd Image = C.jpg
Image Title
Image description
4st Image = D.jpg
Image Title
Image description
5th Image = E.jpg
Image Title
Image description
6th Image = F.img
Image Title
Image description
Now if I changed 2nd image B.jpg
to b.jpg
b.jpg moves to the very end of the list and you have the order as A, C, D, E, F, b
以下是我的模型:
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=250, unique=True)
slug = models.SlugField(allow_unicode=True, unique=True,max_length=500)
post_image = models.ImageField()
message = models.TextField()
class Prep (models.Model): #(Images)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post_prep')
image = models.ImageField(upload_to='images/', blank=True, null=True, default='')
image_title = models.CharField(max_length=100, default='')
image_description = models.CharField(max_length=250, default='')
sequence = models.SmallIntegerField() ###########################ADDED THIS
class Meta: ###########################ADDED THIS
unique_together = (('post', 'sequence'),) ###########################ADDED THIS
ordering = ['sequence'] ###########################ADDED THIS
我的 post 创建视图
def post_create(request):
ImageFormSet = modelformset_factory(Prep, fields=('image', 'image_title', 'image_description'), extra=12, max_num=12,
min_num=2)
if request.method == "POST":
form = PostForm(request.POST or None, request.FILES or None)
formset = ImageFormSet(request.POST or None, request.FILES or None)
if form.is_valid() and formset.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
post_user = request.user
for index, f in enumerate(formset.cleaned_data): #######CHANGED THIS
try: ##############CHANGED THIS
photo = Prep(sequence=index, post=instance, image=f['image'],
image_title=f['image_title'], image_description=f['image_description'])
photo.save()
except Exception as e:
break
return redirect('posts:single', username=instance.user.username, slug=instance.slug)
else:
form = PostForm()
formset = ImageFormSet(queryset=Prep.objects.none())
context = {
'form': form,
'formset': formset,
}
return render(request, 'posts/post_form.html', context)
我的 post 编辑视图:
class PostPrepUpdate(LoginRequiredMixin, UpdateView):
model = Post
fields = ('title', 'message', 'post_image')
template_name = 'posts/post_edit.html'
success_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
data = super(PostPrepUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['prep'] = PrepFormSet(self.request.POST, self.request.FILES, instance=self.object)
else:
data['prep'] = PrepFormSet(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
prep = context['prep']
with transaction.atomic():
self.object = form.save()
if prep.is_valid():
prep.instance = self.object
prep.save()
return super(PostPrepUpdate, self).form_valid(form)
我的Forms.py
class PostEditForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'message', 'post_image', 'group', )
class PrepForm(forms.ModelForm): #####################CHANGED THIS
class Meta:
model = Prep
fields = ('image', 'image_title', 'image_description', 'sequence')
PrepFormSet = inlineformset_factory(Post, Prep, form=PrepForm, extra=5, max_num=7, min_num=2)
***Need help fixing this issue. Example if they change Image 2. Then it should stay at Number 2 position and not move to the end of the list
目前您不保存图像的顺序,因为它们的显示顺序与创建顺序相同。在 Prep
中添加一个包含图像在图像序列中的位置的字段将有助于:
class Prep (models.Model):
# ...
nr = SmallIntegerField()
#...
class Meta:
unique_together = (('post', 'nr'),)
unique_together
约束确保每个数字在每个 post 中只使用一次。这也允许在 post 中重新排序图像,而无需删除和重新创建所有 Prep
个对象。
在显示 post 时,您必须按 nr
对 Prep
个对象进行排序。
至于填充新列,由于没有有意义的单一默认值,最简单的方法可能是:
- 添加没有
unique_together
约束的nr
字段,首先是null=True
;迁移更改。
- 迁移后,按当前顺序遍历每个
Post
现有的Prep
个对象,并为它们分配升序编号。
- 之后删除
null=True
,添加 unique_together
并再次迁移。
unique_together
需要字符串参数(无法访问外层class中的字段);感谢您收听。
在编辑表单中,您希望包含新字段,以便用户可以通过交换索引来交换两个图像的顺序。如果他们使用重复索引,您只需向他们提供有意义的错误消息。
然而,在创建时,您的用户不需要明确指定顺序,因为它隐含在表单集中的图像序列中。所以我建议像这样更改您的 for 循环:
for index, f in enumerate(formset.cleaned_data):
# ...
photo = Prep(nr=index,
# add all other fields
)
对以 1 开头的人性化索引使用 nr = index + 1
。事实上,index
或 image_index
可能是比 nr
更好的字段名称。
假设我正在制作一个 "How to" Django webapp,用户可以在其中制作 post关于如何-做不同的事情。
- "How to"做个绳子
- "How to"做个土锅
- "How to"学骑自行车
你懂的。当成员使post.They向post
添加额外的图像时,我已经使post为this.Now创建视图例子: "How to"做一根绳子
- 这有Post标题=如何制作绳子
- Post 描述="Some description"
- Post 图片 = 主图
现在他们必须一步一步地展示绳子是如何制作的
- 图 1:第 1 个
- 图 2:第二次这样做
我使用 Django 表单集和我的 post 模型来实现这一点。在创建视图中一切都工作得很好。没问题。但在更新视图中,事情发生了变化。
The Problem
问题是当用户想要编辑他们的 post 并将图像编号 2 从他们的 post 切换到不同的图像时。即使他们改变了第二张图片。该图像现在位于列表的最后。使用户 re-upload 所有图像。带回订单。让我的应用看起来有问题。
示例: 假设用户具有以下 post
main post Title
" Some description "
Main Image = Post_image.jpg
1st Image = A.jpg
Image Title
Image description
2nd Image = B.jpg
Image Title
Image description
3rd Image = C.jpg
Image Title
Image description
4st Image = D.jpg
Image Title
Image description
5th Image = E.jpg
Image Title
Image description
6th Image = F.img
Image Title
Image description
Now if I changed 2nd image
B.jpg
tob.jpg
b.jpg moves to the very end of the list and you have the order as A, C, D, E, F, b
以下是我的模型:
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=250, unique=True)
slug = models.SlugField(allow_unicode=True, unique=True,max_length=500)
post_image = models.ImageField()
message = models.TextField()
class Prep (models.Model): #(Images)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='post_prep')
image = models.ImageField(upload_to='images/', blank=True, null=True, default='')
image_title = models.CharField(max_length=100, default='')
image_description = models.CharField(max_length=250, default='')
sequence = models.SmallIntegerField() ###########################ADDED THIS
class Meta: ###########################ADDED THIS
unique_together = (('post', 'sequence'),) ###########################ADDED THIS
ordering = ['sequence'] ###########################ADDED THIS
我的 post 创建视图
def post_create(request):
ImageFormSet = modelformset_factory(Prep, fields=('image', 'image_title', 'image_description'), extra=12, max_num=12,
min_num=2)
if request.method == "POST":
form = PostForm(request.POST or None, request.FILES or None)
formset = ImageFormSet(request.POST or None, request.FILES or None)
if form.is_valid() and formset.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
post_user = request.user
for index, f in enumerate(formset.cleaned_data): #######CHANGED THIS
try: ##############CHANGED THIS
photo = Prep(sequence=index, post=instance, image=f['image'],
image_title=f['image_title'], image_description=f['image_description'])
photo.save()
except Exception as e:
break
return redirect('posts:single', username=instance.user.username, slug=instance.slug)
else:
form = PostForm()
formset = ImageFormSet(queryset=Prep.objects.none())
context = {
'form': form,
'formset': formset,
}
return render(request, 'posts/post_form.html', context)
我的 post 编辑视图:
class PostPrepUpdate(LoginRequiredMixin, UpdateView):
model = Post
fields = ('title', 'message', 'post_image')
template_name = 'posts/post_edit.html'
success_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
data = super(PostPrepUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['prep'] = PrepFormSet(self.request.POST, self.request.FILES, instance=self.object)
else:
data['prep'] = PrepFormSet(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
prep = context['prep']
with transaction.atomic():
self.object = form.save()
if prep.is_valid():
prep.instance = self.object
prep.save()
return super(PostPrepUpdate, self).form_valid(form)
我的Forms.py
class PostEditForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'message', 'post_image', 'group', )
class PrepForm(forms.ModelForm): #####################CHANGED THIS
class Meta:
model = Prep
fields = ('image', 'image_title', 'image_description', 'sequence')
PrepFormSet = inlineformset_factory(Post, Prep, form=PrepForm, extra=5, max_num=7, min_num=2)
***Need help fixing this issue. Example if they change Image 2. Then it should stay at Number 2 position and not move to the end of the list
目前您不保存图像的顺序,因为它们的显示顺序与创建顺序相同。在 Prep
中添加一个包含图像在图像序列中的位置的字段将有助于:
class Prep (models.Model):
# ...
nr = SmallIntegerField()
#...
class Meta:
unique_together = (('post', 'nr'),)
unique_together
约束确保每个数字在每个 post 中只使用一次。这也允许在 post 中重新排序图像,而无需删除和重新创建所有 Prep
个对象。
在显示 post 时,您必须按 nr
对 Prep
个对象进行排序。
至于填充新列,由于没有有意义的单一默认值,最简单的方法可能是:
- 添加没有
unique_together
约束的nr
字段,首先是null=True
;迁移更改。 - 迁移后,按当前顺序遍历每个
Post
现有的Prep
个对象,并为它们分配升序编号。 - 之后删除
null=True
,添加unique_together
并再次迁移。
unique_together
需要字符串参数(无法访问外层class中的字段);感谢您收听。
在编辑表单中,您希望包含新字段,以便用户可以通过交换索引来交换两个图像的顺序。如果他们使用重复索引,您只需向他们提供有意义的错误消息。
然而,在创建时,您的用户不需要明确指定顺序,因为它隐含在表单集中的图像序列中。所以我建议像这样更改您的 for 循环:
for index, f in enumerate(formset.cleaned_data):
# ...
photo = Prep(nr=index,
# add all other fields
)
对以 1 开头的人性化索引使用 nr = index + 1
。事实上,index
或 image_index
可能是比 nr
更好的字段名称。