Reputation: 109
I want to receive several image groups in one form and save them. I want to save each image group with the same main title and different title. How can I save and target images files on views.py?
I didn't hand over the form to the request from views.py because the user could add and delete the content. Is there a way to use a form tag when I want to receive data that I don't know how many?
views.py
def debate_create(request):
if request.method == "POST":
content = request.POST.items()
for k,v in content:
if k == 'sup_title':
sup_title = SuperTitle()
sup_title.author = request.user
sup_title.super_title = v
sup_title.save()
sup_title_id = sup_title.id
elif 'img' not in k and 'opt' not in k and 'section' in k:
sub_title = Subtitle()
sub_title.super_title = get_object_or_404(SuperTitle, pk = sup_title_id)
sub_title.sub_title = v
sub_title.save()
elif 'img' in k and v != '':
stg = Images()
# imgs = request.FILES.getlist("?????")
# for image in imgs
stg.sub_title = get_object_or_404(Subtitle, pk = sub_title.id)
stg.save()
elif 'section' in k and 'opt' in k:
opt = SelectOption()
opt.sub_title = get_object_or_404(Subtitle, pk = sub_title.id)
opt.option = v
opt.save()
return render(request, 'polls/test.html')
else:
pass
html
<form method="POST" id="debate_form" action="{% url 'polls:debate_create' %}">
{% csrf_token %}
<input type='text' name='sup_title' placeholder='제목'>
<div id="form_container">
<section id='section_1'>
<input type="text" name="section_1">
<input type="file" name="img_section_1" multiple>
<div id="section_1_div">
<input type="text" name="section_1_opt_1" value="찬성">
<input type="text" name="section_1_opt_2" value="반대">
</div>
<input type="button" value="선택지 추가하기" onclick='add_option(this.id)' id="section_1">
<input type="button" value="선택지 삭제하기" onclick='sub_option(this.id)' id="section_1">
</section>
</div>
<input type="button" value='질문 추가하기' onclick='add_content()'>
<input type="button" value='질문 삭제하기' onclick='sub_content()'>
<input type="submit">
</form>
Upvotes: 0
Views: 941
Reputation: 771
There is of course a way to achieve this in Django, and it is called model formsets.
Suppose your models are defined as such
# models.py
from django.db import models
class SuperTitle(models.Model):
super_title = models.CharField(max_length=50)
author = models.ForeignKey(User, on_delete=models.PROTECT)
class Subtitle(models.Model):
super_title = models.ForeignKey(SuperGroup, on_delete=models.CASCADE)
subtitle = models.CharField(max_length=50)
image = models.ImageField(upload_to='path/to/uploads/')
# I changed opt from a model to a field of Subtitle,
# since it has only 2 values in your example.
# Feel free to change it back.
opt = models.IntegerField(choices=[(1,"찬성"), (2, "반대")])
Then in your views
# views.py
from django import forms
from .models import SuperTitle, Subtitle
def debate_create(request):
SuperTitleForm = forms.modelform_factory(SuperTitle, fields=['super_title'])
SubtitleFormSet = forms.modelformset_factory(Subtitle, fields=['subtitle', 'image', 'opt'])
if request.method == 'POST':
super_title_form = SuperTitleForm(request.POST)
# NOTE uploaded files are available in request.FILES
subtitle_formset = SubtitleFormSet(request.POST, request.FILES, queryset=Subtitle.objects.none())
if super_title_form.is_valid() and subtitle_formset.is_valid():
super_title = super_title_form.save()
subtitles = subtitle_forms.save(commit=False)
for subtitle in subtitles:
subtitle.super_title = super_title
Subtitle.objects.bulk_create(subtitles)
else:
super_title_form = SuperTitleForm()
subtitle_formset = SubtitleFormSet(queryset=Subtitle.objects.filter(super_title__author=request.user))
context = {
'super_title_form': super_title_form,
'subtitle_formset': subtitle_formset,
}
return render(request, 'polls/test.html', context)
Please note that I pass queryset=Subtitle.objects.none()
to formset. This will make formset only deal with new subtitles. If you want to edit or even delete existing subtitles, change the queryset to, for example, queryset=Subtitle.objects.filter(super_title__author=request.user)
. Django recommends the queryset being ordered.
And you probably need to do similar thing to super_title_form
so that it knows which super_title your are editing.
Please also note that there are two required hidden field when rendering formset manually:
{{ subtitle_formset.management_form.TOTAL_FORMS }}
{{ subtitle_formset.management_form.INITIAL_FORMS }}
Personally, I like to put these 2 alongside {% csrf_token %}
, in case not forgetting them.
Edited: one more thing for your 1st question
You can customize your image store path like following
def subtitle_image_path(instance, filename):
return f'uploads/{instance.super_title.id}/{filename}'
class Subtitle(models.Model):
..
image = models.ImageField(upload_to=subtitle_image_path)
and django will automatically save the image to, say, uploads/1/img01.png
when you call form.save()
(or subtitle.save()
in this case).
Upvotes: 1