multiple forms in one step, Django Wizard form - django

I am trying to create 2 Model Forms in on Step, one of them is Modelformset, does someone did this before, it will be very helpful.
I am using Django 2 in my project.
Thank you.
class Post(models.Model):
main_image = models.ImageField('main_image', upload_to='main_images/', blank=True, null=True)
def __str__(self):
return str(self.pk)
class PostImages(models.Model):
image = models.ImageField('Foto', upload_to='post_images/', blank=True, null=True)
post = models.ForeignKey(Post,related_name='myposts',on_delete=models.CASCADE, null=True)
def __str__(self):
return str(self.pk)
my Forms.py
Here I am Trying to create two forms, first one is main_image and the other is formsetfield in one step. that works but i can not get the instance of my formset so i can not save it.
class step3Form(forms.ModelForm):
main_image = forms.FileField(widget=forms.FileInput(attrs={'class': 'custom-control'}), required=True, label=_('Hauptbild:'))
formsetfield = modelformset_factory(PostImages, ImageForm, can_delete=True)
class Meta:
model = Post
fields = ('main_image',)
def __init__(self, *args, **kwargs):
super(step3Form, self).__init__(*args, **kwargs)
views.py
what schuld I do here to get the instance of my Formset? in order to save it!
class PostCreateView(LoginRequiredMixin, SessionWizardView):
instance = None
form_list = [PostForm, PostFormSecondStep, step3Form, step4Form]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_form_instance(self, step):
if self.instance is None:
self.instance = Post()
return self.instance
def done(self, form_list, form_dict, **kwargs):
form_data_dict = self.get_all_cleaned_data()
result = {}
self.instance.author = self.request.user.profile
form_list[2].save()
self.instance.save()
form_data = [form.cleaned_data for form in form_list]
return redirect('post_app:post_create_page')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
html
<form method="post" id="msform" enctype="multipart/form-data">
{% csrf_token %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{% if wizard.form.non_field_errors %}
<ul>
{% for error in wizard.form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{{ wizard.form.main_image.label_tag }}
{{ wizard.form.main_image }}
{{ wizard.form.main_image.errors }}
{{ wizard.form.formsetfield.errors }}
<div id="formset" data-formset-prefix="{{ wizard.form.formsetfield.prefix }}">
{{ wizard.form.formsetfield.management_form }}
<div data-formset-body>
{% for form in wizard.form.formsetfield %}
{{ form.non_field_errors }}
{{ form.errors }}
<div data-formset-form>
{{ form }}
<span class="p-2"><a class="cursor-pointer" data-formset-delete-button><i class="far fa-trash-alt"></i></a></span>
</div>
{% endfor %}
</div>
<input type="button" class="btn btn-outline-dark" value="Hinzufügen" data-formset-add>
</div>
{% endif %}
<button type="submit" id="submit_btn" class="action-button" name="button">Weiter</button>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" class="action-button" type="submit" value="{{ wizard.steps.first }}">Erster Schritt</button>
<button name="wizard_goto_step" id="back_btn" class="action-button" type="submit" value="{{ wizard.steps.prev }}">Zurück</button>
{% endif %}
</form>

try this in function based:
Forms.py
class step3Form(forms.ModelForm):
class Meta:
model = Post
fields = ('main_image',)
view.py
from django.forms import modelformset_factory
def create(request):
formset = modelformset_factory(
Post,
form=step3Form,
fields=('main_image'),
extra=1,
can_delete=True
)
if request.method == 'POST':
form = formset(request.POST, prefix='post')
if form.is_valid()
post = form.save(commit=False)
for row in post:
row.userID = request.user
row.save()
form.save()
context['form']=formset(queryset=Post.objects.none(), prefix='post')
return render(request, 'post/form.html',context=context)

Related

Django DeleteView derived CBV is deleting my object without showing confirm delete page

I am using Django 3.2
I have a model Foo, and I have written a CBV to allow for deletion. This is what my CBV looks like:
/path/to/foo/view.py
class FooDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Foo
slug_url_kwarg = 'identifier'
slug_field = 'identifier'
success_url = reverse_lazy('homepage')
def get_object(self, queryset=None):
identifier = self.kwargs[self.slug_field]
return self.model.objects.get(identifier=identifier)
def test_func(self):
photo = self.get_object()
return self.request.user == foo.owner
def post(self, request, *args, **kwargs) -> HttpResponse:
# foo object is needed for some other logic (audit trail etc.)
foo = self.get_object()
# ....
return super().post(request, *args, **kwargs)
/path/to/foo/templates/foo/templates/foo_confirm_delete
{% extends 'base.html' %}
{% load static %}
{% block page_title %}Foo deletion Confirmation {% endblock %}
{% block content %}
<form action="{% url 'myapp:foo-delete' %}" method="post">{% csrf_token %}
<div class="form-group">
Are you sure you want to delete this item?<br>
<br>
<strong>{{ object }}</strong>
{{ form.errors }}
<br>
<br>
<input type="hidden" name="confirm_delete" value="confirm_delete">
<p>
<input type="submit" class="btn btn-primary">Delete</input>
Cancel
</p>
</div>
</form>
{% endblock content %}
Why is my foo_confirm_delete.html form not being displayed to give me a chance to cancel - before the object is deleted? How do I fix this?

Using two submit buttons to save django inlineformset as draft

I have an inlineformset that works. I have attempted to add a second submit button, that when clicked will check that a particular field in each inline form has been filled out i.e. this field becomes a required filed only when the second submit button is clicked.
The problem is that when I click this second submit button the validation errors don't appear and the form just seems to submit anyway. I think the issue is within my view, within form_valid. I'm not sure what I need to return when if form.is_valid() fails.
I'm teaching myself to code, so any help or direction is much appreciated.
Forms.py
class response_form_draft(forms.ModelForm):
class Meta:
name = response
exclude = ['id_question','order']
def __init__(self, *args, **kwargs):
super(response_form_draft, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
class response_form_final(forms.ModelForm):
class Meta:
name = response
exclude = ['id_question','order']
def __init__(self, *args, **kwargs):
super(response_form_final, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
def clean(self):
data = self.cleaned_data
resp = data['response']
if resp == "":
raise forms.ValidationError(('must not be blank'))
checklist_formset_draft = inlineformset_factory(checklist, response,
form = response_form_draft,
extra=0, can_delete=False,
widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
'style':'resize:none'})
})
checklist_formset_final = inlineformset_factory(checklist, response,
form = response_form_final,
extra=0, can_delete=False,
widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
'style':'resize:none'}),
'response': forms.TextInput(attrs={'required':True})
})
Views.py
class ChecklistUpdateView(LoginRequiredMixin, UpdateView):
login_url = '/user/login'
model = checklist
form_class = checklist_form
success_url = reverse_lazy('checklist:checklist_list')
def get_context_data(self, **kwargs):
data = super(ChecklistUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
if 'complete' in self.request.POST:
print("complete")
data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
else:
print("draft")
data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
question = context['question']
with transaction.atomic():
self.object = form.save(commit=False)
self.object.last_edit_by = str(self.request.user)
self.object.last_edit_date = timezone.now()
if question.is_valid():
question.instance = self.object
question.save()
return super(ChecklistUpdateView, self).form_valid(form)
HTML template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post">
{% csrf_token %}
<h1>{{ object.id_template.title }}</h1>
{{ form.errors }}
{{ form.entity|as_crispy_field }}
{{ form.date_created.as_hidden }}
{{ form.created_by.as_hidden }}
{{ form.id_template.as_hidden }}
{{ form.status.as_hidden }}
<div class="card">
<table id="table_id" class="table">
<tbody>
{{ question.management_form }}
{% for form in question.forms %}
{{ formset.errors }}
{{ form.non_field_errors }}
{% if forloop.first %}
<thead class="thead-dark">
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
<th></th>
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1 row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{% if field.label == "Question" %}
<p>{{ form.question.value }}</p>
{{ field.as_hidden }}
{% elif field.label == "Response" %}
{{ field.as_hidden }}
<button id="Yes.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">Yes</button>
<button id="No.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">No</button>
<button id="N/a.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">N/a</button>
{% else %}
{{ field|as_crispy_field }}
{% endif %}
</td>
{% endfor %}
<td> </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{{ form.comments|as_crispy_field }}
{{ form.audit|as_crispy_field }}
<div class="form-submit-row" id="submit-row">
<input class="btn btn-dark right" name="draft "type="submit" value="Save draft">
<input class="btn btn-dark right" name="complete" type="submit" value="Complete">
Delete
</div>
</form>
<br><br>
</div>
form_valid is activated when your form is valid. In your classview django automatically check validation for your checklist_form without validating formset. you can add custom validation in your form_valid and form_invalid, but i would go another way. i would put formset inside your base form (checklist_form)
something like that:
class checklist_form(forms.ModelForm):
def __init__(*args, **kwargs):
self.question= kwargs.pop("question")
super()__.init__(*args, **kwargs)
def clean(self):
cleaned_data = super().clean()
if not self.question.is_valid():
self.add_error(None, "Have some formset errors")
return cleaned_data
now form will be valid only if formset is also valid. And then in your ChecklistUpdateView i would put creation of formset into get_form_kwargs
def get_form_kwargs(self):
data = super().get_form_kwargs()
if self.request.POST:
if 'complete' in self.request.POST:
data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
else:
data['question'] = checklist_formset_draft(instance=self.object)
return data
After that in your html template you will need to use form.question instead of question

Django: form not displaying in browser

The form doesn't display in the browser. The navbar and submit button show up but no form in between. The problem must be straightforward but I haven't been able to find the issue. Thank you for your help.
views.py
def ProductCreateView(request):
if request.method == 'POST':
form = ProductForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('set_app/product_list.html'))
else:
product_form = ProductForm()
return render(request, 'set_app/product_form.html', {'product_form':product_form})
forms.py
class ProductForm(forms.Form):
class Meta():
model = models.Product
fields = ('code', 'barcode', 'name', 'description', 'brand', 'status')
product_form.html
{% extends "set_app/basic_app_base.html" %}
{% block body_block %}
<h1>
{% if not form.instance.pk %}
Create Product
{% else %}
Update Product
{% endif %}
</h1>
<form method="post">
{% csrf_token %}
{{ product_form.as_p }}
<input type="submit" class="btn btn-primary" value="Submit">
</form>
{% endblock %}
Found the issue:
in forms.py
instead of
class ProductForm(forms.Form):
it should be
class ProductForm(forms.ModelForm):

Django: Updateview inlineformset error - (Hidden field id) This field is required

I am trying to edit an updateview in django that has an inlineformset. Below are the model.py, forms.py, views.py, and the template files. The form is not saving, so I placed a print(actor_form) in the post method in the views.py to see what was happening. I got the following html code:
<input type="hidden" name="actor-TOTAL_FORMS" value="1" id="id_actor-TOTAL_FORMS" /><input type="hidden" name="actor-INITIAL_FORMS" value="1" id="id_actor-INITIAL_FORMS" /><input type="hidden" name="actor-MIN_NUM_FORMS" value="0" id="id_actor-MIN_NUM_FORMS" /><input type="hidden" name="actor-MAX_NUM_FORMS" value="1000" id="id_actor-MAX_NUM_FORMS" />
<tr><td colspan="2"><ul class="errorlist nonfield"><li>(Hidden field id) This field is required.</li></ul></td></tr>
<tr><th><label for="id_actor-0-actorName">ActorName:</label></th><td><input type="text" name="actor-0-actorName" value="www" maxlength="50" id="id_actor-0-actorName" /></td></tr>
<tr><th><label for="id_actor-0-DELETE">Delete:</label></th><td><label for="id_actor-0-DELETE" class="checkbox-inline"><input type="checkbox" name="actor-0-DELETE" id="id_actor-0-DELETE" />
</label><input type="hidden" name="actor-0-id" id="id_actor-0-id" /><input type="hidden" name="actor-0-useCase" id="id_actor-0-useCase" /></td></tr>
In the second line above, you can see - (Hidden field id) This field is required.
I believe this is cause the inline to not save, but I do not know how to fix it.
Here is the models.py
class Profile(models.Model):
profileName = models.CharField(max_length=50)
profileBoundary = models.CharField(max_length=3, choices = BOUNDARY_CHOICES)
class Actor(models.Model):
profile= models.ForeignKey(UseCaseProfile, on_delete=models.CASCADE , null=True, blank=True)
actorName=models.CharField(max_length=50)
here is the forms.py
class ProfileForm(ModelForm):
class Meta:
model=Profile
exclude = ('project',)
class ActorForm(ModelForm):
class Meta:
model=Actor
fields ='__all__'
views.py
class ProUpdateView(UpdateView):
template_name = 'Update.html'
model = Profile
form_class = ProfileForm
def get_success_url(self):
self.success_url = reverse_lazy ('useCaseExtract:UseCaseList', kwargs={'project': self.kwargs['project']})
return self.success_url
def get_context_data(self, **kwargs):
context = super(ProUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['actor_form'] = ActorFormSet(self.request.POST, instance=self.object, prefix='actor')
else:
context['actor_form'] = ActorFormSet(instance=self.object,prefix='actor')
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
actor_form = ActorFormSet(self.request.POST,prefix='actor')
print(actor_form)
if (form.is_valid() and actor_form.is_valid()):
return self.form_valid(form, actor_form)
else:
return self.form_invalid(form, actor_form)
def form_valid(self, form, actor_form):
self.object = form.save()
actor_form.instance = self.object
actor_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, actor_form):
return self.render_to_response( self.get_context_data(form=form, actor_form=actor_form))
template
<form method="POST" enctype="multipart/form-data" style="margin-left: 40px; margin-right: 40px">
{% for hidden_field in form.hidden_fields %}
{% endfor %}
{% csrf_token %}
{{ form.management_form }}
{{ form.non_form_errors }}
<h4>Use Case Profile:</h4>
{% bootstrap_form form %}
<h4>Supported Role(s):</h4>
<div >
{% for hidden_field in actor_form.hidden_fields %}
{% endfor %}
{{ actor_form.management_form }}
{{ actor_form.non_form_errors }}
{% for aform in actor_form %}
<div >
{{ aform.actorName.errors }}
{% bootstrap_field aform.actorName %}
</div>
{% endfor %}
</div>
</div>
<hr style = "border: 0; border-top: 1px solid #999; margin: 1em 0;">
<a style="margin-left: 350px;" class="text-danger" href="{% url 'useCaseExtract:UseCaseList' view.kwargs.project %}"> {% bootstrap_icon "remove" %} Cancel</a>
<input class="btn btn-success" type="submit" value="Update" />
</form>
</div>
<!-- main_container ends here -->
</div>
Make sure to loop over hidden fields if you are manually laying out a formset in a template.
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
Reference:
https://docs.djangoproject.com/es/2.1/topics/forms/#looping-over-hidden-and-visible-fields

Django creating update post function

I want to create a post update/edit function but I have a problem. When I click "Update" button there is no error but there is no change. I think reason of that a mistake that I made in models.py but I can not figure it out. And here is my code
models.py
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
.
.
.
def get_update_url(self):
return reverse('post:post_update', kwargs={'slug': self.slug})
views.py
def post_update(request, slug):
if not request.user.is_authenticated():
return Http404()
post = get_object_or_404(Post, slug=slug)
form = PostForm(request.POST or None, request.FILES or None, instance=post)
if form.is_valid():
form.save()
messages.success(request, "Updated")
return HttpResponseRedirect(get_absolute_url())
context = {
'form': form
}
return render(request, "blog/post_update.html", context)
post_update.html
{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>Form</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
{{ form.media }}
<input class="btn btn-primary" type="submit" value="Create post">
</form>
</div>
</div>
</div>
{% endblock %}
post_detail.html
//update link
<p >update</p>
urls.py
url(r'^(?P<slug>[\w-]+)/update/$', post_update, name="update"),
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
Where is my mistake? What can I do?