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
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?
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
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):
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
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?