Django formset is not getting saved - django

I have a formset and I am trying to save it back. But, when I try to check if formset.is_valid() and save, validation always fails even if nothing in the formset has been changed. I am displaying already existing data using formset and trying to edit it. I don't know where I am going wrong. Can someone please help? :(
views.py
def product(request, product_id):
product = get_object_or_404(Product, pk=product_id)
productform = ProductForm(instance=product)
itemformset = modelformset_factory(Item, form = ItemForm, extra=0)
items = itemformset(queryset = Item.objects.filter(Product=product_id), prefix='items', )
if request.method == 'POST':
productform = ProductForm(request.POST, instance=product)
items = itemformset(request.POST, queryset = Item.objects.filter(Product=product_id), prefix='items', )
if productform.is_valid():
productform.save()
if items.is_valid():
items.save()
else:
print("fail")
context = {
'productform':productform,
'product_id': product_id,
'items':items,
}
return render(request, 'product.html', context )
.html
{% bootstrap_form productform %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#Items"> Items List </a>
</h4>
</div>
<div id="Items" class="panel-collapse collapse">
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Tag</th>
</tr>
<thead>
<tbody>
{{ items.management_form }}
{% for form in items %}
<tr>
{{ form.id}}
<td> {{ form.Name }} </td>
<td> {{ form.Tag }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

You are rendering the form fields individually, but not displaying the errors. First, allow Django to render the forms and get the view working. Once you've done that, you can customise the template if required.
<tbody>
{{ items.management_form }}
{% for form in items %}
{{ form }}
{% endfor %}
</tbody>
See the docs on using formsets in templates rendering form fields manually for more information.
Doing it this way should allow you to see errors in the template. Another option is to print or log productform.errors and items.errors. Your current print("fail") tells you there is a problem, but not what the problem is.

Related

Django object update using htmx and SingleObjectMixin

I'm using htmx for the first time. I have a table where each cell is a grade object. Previous to htmx I made each cell a link to an UpdateView for the object. I am now trying to have the user modify the the object's score field directly in the table using htmx. I'm sure I'm doing several things wrong.
My page loads as expected and the table is displayed as expected. when I type in a cell, I get an error Forbidden 403. CSRF Verification Failed.
The purpose of this post/question is to figure out how to get past this 403 error. Having said that, if it turns out that I'm going down the completely wrong path with using a SingleObjectMixin, please let me know.
View
class GradeChange(SingleObjectMixin, View):
""" view to handle htmx grade change"""
model = Grade
def post(self, request, *args, **kwargs):
grade = self.get_object()
assessment = Assessment.objects.get(grade=grade.pk)
print(assessment)
classroom = Classroom.objects.get(classroom=grade.cblock)
print(classroom)
if grade.score == "EXT" or grade.score=="APP" or grade.score=="DEV" or grade.score=="BEG":
grade.save()
return HttpResponse("S")
else:
return HttpResponse("")
template
<table class="table table-bordered table-sm">
<thead>
<tr>
<th class="col-3" scope="col">Students</th>
{% for obj in objective_list %}
<th class="col-2" scope="col">{{ obj.objective_name }}</th>
{% endfor %}
<th scope="col">Comments</th>
</tr>
</thead>
<tbody>
<form action="" method="post" class="form-group">
{% csrf_token %}
{% for student in student_list %}
<tr>
<td >{{ student.student_first }} {{ student.student_last }}</td>
{% for g in grade_list %}
{% if g.student.id == student.id %}
<td>
<input type="text" hx-post="{% url 'gradebook:grade-change' g.pk %}" hx-target="" hx-trigger="keyup delay:2s" class="form-control score" title={{ g.score }} name="score" id="input-{{ forloop.counter0 }}" placeholder={{ g.score }} required>
</td>
{% endif %}
{% endfor %}
<td>
{% for comms in comment_list %}
{% if comms.student == student %}
<a class="grade-comment" href="{% url 'gradebook:addcomment' comms.pk assess_pk class_pk %}">{{ comms.comment|truncatewords:10 }}</a>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</form>
</tbody>
</table>
EDIT - a solution
I came across this, which works.
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
});
</script>
The CSRF token must be included in the HTMX POST request. Currently you include it only in the regular form, but the HTMX request is initiated from a child input element where the token is not present. So just include the following hx-header attribute to a parent element like the <form> or event the <body> is a good candidate:
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>

form doesn't submit productbacklogs to database

here is my project code .
I after posting data by form nothing happens.
#model.py
from django.db import models
from projectapp.models import Project
class Productbacklog(models.Model):
project=models.ForeignKey(Project,on_delete=models.CASCADE,default=None)
pbId=models.IntegerField(primary_key=True)
pbTitle=models.CharField(max_length=100)
pbPriority=models.IntegerField(blank=True, null=True)
class Meta:
unique_together=('project','pbId')
def __str__(self):
return self.pbTitle
#forms.py
from django import forms
from productbacklogapp.models import Productbacklog
from projectapp.models import Project
class ProductbacklogForm(forms.ModelForm):
class Meta:
model = Productbacklog
exclude=('pbId','project')
fields=['pbTitle']
#views.py
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})
I think that issue is on forms.py or views.py but I can't find it.
I'm so greatful if anyone can help me.
here is also my html code,when I submit something the method is post but I don't know why it doesn't go to data basse.
#productbacklogall.html
{%extends 'base.html'%}
{%block title%}
<title>backlog all</title>
{%endblock title%}
{%block content%}
</br>
{% if messages %}
{% for message in messages %}
<div class="alert alert-primary" role="alert">
{{ message }}
×
</div>
{% endfor %}
{% endif %}
<div class="container">
<form method="POST" class="row">
{% csrf_token %}
<label class="col-lg-4"></label>
<input type="text" class="form-control" name="Project" placeholder="project title?"/>
<input type="number" class="form-control" name="pbId" placeholder="backlog id?"/>
<input type="text" class="form-control" name="pbTitle" placeholder="pb title?"/>
<button type="submit" class="btn btn-primary col-lg-2">add project</button>
</form>
</div>
</br>
</br>
<table class="table table-bordered text-center">
<thead class="thead-dark">
<tr>
<th scope="col"> backlog-title</th>
<th scope="col">Edit</th>
<th scope="col">delivarable_task</th>
<th scope="col">related project</th>
</tr>
</thead>
<tbody>
{% load static %}
{% if pb_all %}
{% for obj in pb_all %}
<tr>
<td>{{ obj.pbTitle }}</td>
<td><a href='{% url "delete_productbacklog" obj.pbId %}'>delete</a></td>
<td> <a href=#> delivarabletask </a></td>
<td>{{ obj.project.pbTitle }}</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
{%endblock content%}
Your form data are not valid, to see them, add this code to your views :
else:
messages.error(request, "Error")
like this :
def productbacklogall(request):
if request.method == 'POST':
form = ProductbacklogForm(request.POST)
if form.is_valid():
form.instance.manage = Project.objects.get_or_create(cname=form.cleaned_data['manage_id'])
form.save()
messages.success(request, ('new productbacklog added'))
else:
messages.error(request, "Error")
return redirect('productbacklogall')
else:
pb_all=Productbacklog.objects.all()
return render(request, 'productbacklogall.html', {'pb_all':pb_all})

got an unexpected keyword argument 'id'

I'm trying to do a query when the object has the status = 'Opened'. And display in a table where I will have a button to give a solution for my form and change status='Pending'. But when I click in the button I get this error.
What I'd like to do for really is display was a form for each data, but when I do a for loop for each one form my data insnt show, how you can see my editable.html. I just get the buttons to do an action, and they are working fine.
url:
path('manutencao_os_status/<int:id>', views.manutencao_ordem_de_servico, name='manutencao_os_status'),
path('manutencao_ordem_de_servico/', views.manutencao_ordem_de_servico, name='manutencao_ordem_de_servico'),
views.py
def manutencao_ordem_de_servico(request):
ordem_servico = OrdemServico.objects.filter(status='Aberto').order_by('id')
form = FormOrdemServico(ordem_servico)
if request.method != 'POST':
return render(request, 'ordemservico/manutencao_ordem_de_servico.html', {
'form': form,
'ordem_servico': ordem_servico
})
form = FormOrdemServico(request.POST, ordem_servico)
if not form.is_valid():
return render(request, 'ordemservico/manutencao_ordem_de_servico.html', {
'form': form,
'ordem_servico': ordem_servico
})
ordem_servico.save()
return redirect('ordemservico:manutencao_ordem_de_servico')
def manutencao_os_status(request, id):
ordem_servico = OrdemServico.objects.get(pk=id)
ordem_servico.status = 'Em Aprovação'
ordem_servico.save()
return redirect('ordemservico:manutencao_os_status')
html:
{%extends 'base.html' %}
{%block conteudo %}
<h1>Ordens de Serviço</h1>
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="card card-primary">
<div class="table table-bordered">
<table class="table table-bordered">
<thead>
<tr>
<td>Condition:</td>
<td>ID:</td>
<td>Name:</td>
<td>Status:</td>
<td>Solution:</td>
</tr>
</thead>
<tbody>
{% for os in ordem_servico %}
<tr>
<td>
<a href="{% url 'ordemservico:manutencao_os_status' os.id %}"
class="btn btn-success">Aprovar</a>
</td>
<td>{{os.id}}</td>
<td> {{os.name}}</td>
<td>{{os.status}}</td>
<td>{{os.solution}}</td>
</tr>
{%endfor%}
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
my editable.html:
{%for os in ordem_servico %}
<form action="{% url 'ordemservico:manutencao_os_status' os.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend><h2>Ordens de Serviço</h2></legend>
<table class="table">
{{ os.form }}
<tr>
<td colspan="2">
<button type="submit" class="btn btn-primary">Solucionar</button>
</td>
</tr>
</table>
</fieldset>
</form>
{%endfor%}
{% endblock %}
You routed to the wrong view, it should be:
path(
'manutencao_os_status/<int:id>',
views.manutencao_os_status,
name='manutencao_os_status'
),
In the view you should likely redirect to the manutencao_ordem_de_servico view:
def manutencao_os_status(request, id):
ordem_servico = OrdemServico.objects.filter(pk=id).update(
status='Em Aprovação'
)
return redirect('ordemservico:manutencao_ordem_de_servico')
Note: A GET request is not supposed to have side-effects, hence updating
objects when a user makes a GET request, is not compliant with the HTTP
standard. Therefore it might be better to update a OrdemServico with a POST request.

custom template form fields django

i tried to use my own template style instead of default django form style , it work fine for creating post but doesnt work for update my formsets (inlineformset) , it also work if i use default django form style
this my views.py
class TitleQuestionAnswer(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
model = Title
form_class = TitleForm
template_name = 'template/update_title_question.html'
def get_context_data(self,*args,**kwargs):
context = super(TitleQuestionAnswer,self).get_context_data(*args,**kwargs)
if self.request.POST:
context['questions'] = QA_InlineFormset(self.request.POST,instance=self.object)
else:
context['questions'] = QA_InlineFormset(instance=self.object)
return context
def form_valid(self,form):
context = self.get_context_data()
context = context['questions']
self.object = form.save()
if context.is_valid() and context.cleaned_data !={}:
response = super().form_valid(form)
context.instance = self.object
context.save()
return response
print('errors : ',context.errors, 'data : ',context.cleaned_data)#updated
return super().form_invalid(form)
def get_success_url(self):
return reverse_lazy('q-answer:post',kwargs={'pk':self.object.pk})
and this is my template
<form method="POST">{% csrf_token %}
<div class="col-6 inp text-center">
{{form.name | add_class:'col-12 text-center'}}
{% if form.name.errors %}
<div class="error col-3 mx-auto">{{form.name.errors}}</div>
{% endif %}
</div>
</div>
<!-- order -->
<div class="col-12 p-0 border-top border-light ">
<table class="customeTable col-12 table-responsive-sm info text-center table1 mx-auto mb-2 ">
<tbody class="tbody tb1 " id="form_set">
{{questions.management_form}}
{% for form in questions.forms %}
{{form.errors}} <!--updated-->
{{questions.errors}}
{{questions.non_form_errors}}
{{form.id}}
<tr class="p-0 col-12">
<td class="">
<div class="col-12 p-0 mt-3 inp">
{{form.field_a | add_class:'col-12'}}
</div>
</td>
<td class="">
<div class="col-12 p-0 mt-3 inp">
{{form.field_b | add_class:'col-12'}}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</form>
but if i change it to only {{questions.management_form}} {% for form in questions.forms %} {{form}}{%
endfor %}
this style works fone for my CreateView !
it will work fine !? is there something i missed ? or should i add or remove something in my code ?
i appreciate your helps ..

Django: Updating and Deleting objects using Django Views

The task which I am trying to achieve is to give the power to the logged-in user to update and delete their contact list via Django views. I coded all the templates and views.py functionality but has not been able to proceed forward.
Here is what I was able to achieve:
"views.py":
#login_required
def update_contact(request,contact_id=1):
current_user = request.user.get_username()
user = User.objects.filter(username=current_user).first()
output = UserContacts.objects.filter(current_user_id=user.id)
count = output.count()
contact_obj =get_object_or_404(UserContacts,id = contact_id)
form = UserContactForm()
if request.method == "POST":
form = UserContactForm(request.POST,instance=contact_obj)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print('Error Form Invalid')
my_dict = {'output':output,'form':form}
return render(request,'basic_app/update.html',my_dict)
Here is my template for update.html for updating contact:
{%extends 'basic_app/base.html' %}
{% block body_block %}
<div class="jumbotron">
<h1>Welcome to update contact page</h1>
<br />
{% if output %}
<table class="table table-striped">
<thead class="thead-dark">
<th>First Name</th>
<th>Edit</th>
</thead>
{% for contact in output %}
<tr>
<td>{{ contact.first_name}}</td>
<td>Edit</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-warning" role="alert">
No contact records found!
</div>
{% endif %}
</div>
{% endblock %}
URLS.Py:
app_name = 'basic_app'
urlpatterns = [
url(r'^register/$',views.register,name='register'),
url(r'^user_login/$',views.user_login,name='user_login'),
url(r'^new_contact/$',views.new_contact,name='new_contact'),
url(r'^view_contacts/$',views.view_contacts,name='view_contacts'),
url(r'^update/(?P<contact_id>[\d]+)$',views.update_contact,name='update'),
url(r'^delete/$',views.delete_contact,name="delete")
]
Here is the error I am currently stuck with
Any help would be greatly appreciated!
In your urls.py the name of the update url is update:
url(r'^update/(?P<contact_id>[\d]+)$',views.update_contact,name='update')
But in your template your are using update_contact as name.
<td>Edit</td>
Try first to change the name in the template:
<td>Edit</td>