I am working on a project using modelformset to render multiple instance of a model and whenever I try to update my data, I get the error below.
Template error:
In template /home/dubsy/virtualenvs/djangoproject/libmain/templates/books/approve.html, error at line 67
Field 'id' expected a number but got ''.
57 : <thead>
58 : <tr>
59 : <th>Book Title</th>
60 : <th>Approved</th>
61 : <th>Not Approved</th>
62 : </tr>
63 : </thead>
64 : <tbody>
65 : {% for form in formset %}
66 : <tr>
67 : <td> {{ form.instance.book.title }} </td>
68 : <td>{{ form.approved }}</td>
69 : <td>{{ form.not_approved }}</td>
70 : </tr>
71 : {% endfor %}
72 : </tbody>
73 : </table>
74 : <button type="submit">Update</button>
75 : </form>
76 :
77 : </body>
Exception Type: ValueError at /approve/2/
Exception Value: Field 'id' expected a number but got ''.
I have tried using the users context in views.py to render out the form which works fo book title but doesn't work for the two input field as it renders out the value from
database instead of a checkbox input field.
views.py:
def approve(request,pk):
users = PendingRequest.objects.filter(member__id=pk)
RequestFormset = modelformset_factory(PendingRequest, fields=("approved", "not_approved"),extra=0)
if request.method == "POST":
formset = RequestFormset(request.POST, queryset=users)
if formset.is_valid():
formset.save()
else:
formset = RequestFormset(queryset=users)
return render(request, "books/approve.html",{"formset":formset, "users":users})
template.html:
<form method="post" action="{% url 'approve' pk=users.0.member.id %}">
{% csrf_token %}
{{ formset.management_form }}
<table>
<thead>
<tr>
<th>Book Title</th>
<th>Approved</th>
<th>Not Approved</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
<td>{{ form.instance.book.title }}</td>
<td>{{ form.approved }}</td>
<td>{{ form.not_approved }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Update</button>
</form>
models.py:
class PendingRequest(models.Model):
book_request = models.ForeignKey(BorrowBook, on_delete=models.CASCADE, null=True)
member = models.ForeignKey(User, on_delete=models.CASCADE, default="", null=True)
book = models.ForeignKey(Books, on_delete=models.CASCADE, default="", null=True)
approved = models.BooleanField(default=False)
not_approved = models.BooleanField(default=False)
approval_date = models.DateTimeField(auto_now=True, null=True)
urls.py:
urlpatterns = [
path("approve/<int:pk>/", views.approve, name="approve"),
]
Any ideas?
Related
I am creating an app for a school schedule using Django. Admin users can currently add classes to a list. Student users can view that list but I want to create a button/link that will add that class object to a separate list that will act as their applied classes.
Models.py
class Class(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=50)
crn = models.CharField(max_length=5, validators=[RegexValidator(r'^\d{1,10}$')])
grade_mode = models.CharField(max_length=50)
subject = models.CharField(max_length=3)
course_num = models.CharField(max_length=4, validators=[RegexValidator(r'^\d{1,10}$')])
section_num = models.CharField(max_length=3, validators=[RegexValidator(r'^\d{1,10}$')])
credit_hours = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(5.000)])
teacher = models.CharField(max_length=50)
Views.py
#login_required
#dec.student_required
def index(request):
class_list = Class.objects.all().order_by("-name")
# Allow for Posting of New Classes to Schedule.
if request.method == "POST":
form = ClassesForm(request.POST)
if form.is_valid():
form.save()
form = ClassesForm()
messages.success(request, "Class Added to Registry!")
else:
form = ClassesForm()
context_dict = {'classes': class_list,
'form': form}
return render(request, 'index.html', context=context_dict)
Index.HTML Add Button
<table>
<tbody>
{% if classes %}
{% for class in classes %}
<tr>
<td>Class Name: </td>
<td>Subject: </td>
<td>CRN: </td>
<td>Course Number: </td>
<td>Section Number: </td>
<td>Credit Hours: </td>
<td>Teacher: </td>
<td>Grade Mode: </td>
<td>Add Class</td>
</tr>
<tr>
<td>{{ class.name }}</td>
<td>{{ class.subject }}</td>
<td>{{ class.crn }}</td>
<td>{{ class.course_num }}</td>
<td>{{ class.section_num }}</td>
<td>{{ class.credit_hours }}</td>
<td>{{ class.teacher }}</td>
<td>{{ class.grade_mode }}</td>
<td>
<form method="GET">
{% csrf_token %}
<button class="addToSchedule" type="submit" value="add" name="Add Class">
Add Class
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
{% else %}
<strong>There are no classes added.</strong>
{% endif %}
</table>
Image on Website.
So far I can print out the models on their own separate list as shown above but I don't know where to go in regards to the button or if a button is even the correct way of doing it. I want to click on a button and that model will be added and saved to the Added Classes section above.
I`ve got a table displaying all products. I would like to display the product attribute for each product name. Is it possible to do this in one table like shown below?
Thank you
models.py
class Product(models.Model):
name = models.CharField(max_length=60, unique=True)
attribute = models.ManyToManyField(ProductAttribute)
class ProductAttribute(models.Model):
property = models.CharField(max_length=20) # eg. "resolution"
value = models.CharField(max_length=20) # eg. "1080p"
file.html
{% for product in products %}
<tr>
<td style="font-weight:bold">{{ product.name }}</td>
<td>{{ product.productattribute }}</td>
</tr>
{% endfor %}
views.py
#login_required(login_url="/login/")
def productlist_details(request, shop_id, productlist_id):
shop = Shop.objects.get(pk=shop_id)
products = Product.objects.all()
productattributes = ProductAttribute.objects.all()
context = {
'shop': shop,
'products': products,
'productattributes': productattributes,
}
return render(request, 'productlist_details.html', context)
Using the ifchanged tag (https://docs.djangoproject.com/en/3.1/ref/templates/builtins/#ifchanged) you could do something like:
{% for product in products %}
<tr>
<td style="font-weight:bold">{{ product.name }}</td>
</tr>
{% for attribute in product.attributes.all %}
<tr>
<td>{{ attribute.property }}</td>
</tr>
{% endfor %}
{% endfor %}
try this
{% for product in products %}
<tr>
<td style="font-weight:bold">{{ product.name }}</td>
<td>{{ product.attributes.property }}</td>
</tr>
{% endfor %}
I am trying to show a detailed view of the contacts stored in a phonebook. The PhoneBook(id, name) is one of my models which is a foreign key to model Contact(id, first_name, last_name, phone_number, phone_book).
In my index page, there is a button which opens the phone book. After that, I want it such that the user may click on a phone book and the detailed view(first_name, last_name, phone_number) would be shown to them.
In view.py, I have a function which captures all the phonebook, passes it through context(dict). In my template, I have used a for loop to go through all the phonebooks and print them.
I am unable to direct the page to a detailed view. How do I get the phonebook the user clicked on? And how to direct the page from ./view to ./detail
# view.py
def view_phone_book(request):
all_phone_books = PhoneBook.objects.all()
context = {
'all_phone_books': all_phone_books
}
return render(request, "CallCenter/view_phone_book.html", context)
def detailed_view_phone_book(request):
all_contacts = Contact.objects.all().filter(phone_book=phone_book_user_clicked_on)
context = {
'all_contacts': all_contacts
}
return render(request, "CallCenter/detailed_view_phone_book.html", context)
# urls.py
urlpatterns = [
path('', index, name="index"),
path('create/', create_phone_book, name="create"),
path('add/', add_to_phone_book, name="add"),
path('view/', view_phone_book, name="view"),
path('detail/', detailed_view_phone_book, name="detailed_view")
]
# models.py
class PhoneBook(models.Model):
"""
Model to store customer to a phone book
"""
name = models.CharField(max_length=10, blank=False)
def __str__(self):
return self.name
class Contact(models.Model):
"""
Model to store customer to a phone book.
"""
first_name = models.CharField(max_length=50, blank=False)
last_name = models.CharField(max_length=50, blank=False)
phone_number = models.CharField(max_length=13, blank=False, unique=True)
phone_book = models.ForeignKey(PhoneBook, on_delete=models.CASCADE)
def __str__(self):
return self.phone_number
<!--view_phone_book.html-->
<table>
<tr>
<th>Phone Book</th>
</tr>
{% for phone_book in all_phone_books %}
<tr>
<form method="get" action="../detail/"><td>{{ phone_book }} </td></form>
</tr>
{% endfor %}
</table>
<!--detailed_view_phone_book.html-->
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Phone Number</th>
</tr>
{% for phone_detail in all_phone_detail %}
<tr>
<form>
<td>{{ phone_detail.first_name }}</td>
<td>{{ phone_detail.last_name }}</td>
<td>{{ phone_detail.phone_number }}</td>
</form>
</tr>
{% endfor %}
</table>
I am unable to go from ./view to ./detail. Also, how would I know which phone book the user clicked on?
I figured it out on how to make it work, and I'm answering it so that if anyone gets stuck in, it can help themselves.
# views.py
def view_phone_book(request):
all_phone_books = PhoneBook.objects.all()
context = {
'all_phone_books': all_phone_books
}
return render(request, "CallCenter/view_phone_book.html", context)
def detailed_view_phone_book(request, phone_book_id):
try:
all_contacts = Contact.objects.filter(pk=phone_book_id)
except Contact.DoesNotExist:
raise Http404("PhoneBook Does Not Exist!")
context = {
'all_contacts': all_contacts
}
return render(request, "CallCenter/detailed_view_phone_book.html", context)
#urls.py
urlpatterns = [
path('', index, name="index"),
path('create/', create_phone_book, name="create"),
path('add/', add_to_phone_book, name="add"),
path('campaign/', create_campaign, name="create-campaign"),
path('view/', view_phone_book, name="view-phone-book"),
path('detail/<int:phone_book_id>', detailed_view_phone_book, name="detail-view-phone-book"),
<!--view_phone_book.html-->
<body>
{% for phone_book in all_phone_books%}
{{ phone_book }}
<br>
{% endfor %}
Back To Home
</body>
<!--detailed_view_phone_book.html-->
{% if all_contacts %}
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Phone Number</th>
</tr>
{% for contact in all_contacts %}
<tr>
<form>
<td>{{ contact.first_name }}</td>
<td>{{ contact.last_name }}</td>
<td>{{ contact.phone_number }}</td>
</form>
</tr>
{% endfor %}
</table>
{% endif %}
Back To Home
I watched the Brain's CS50 video, which helped me. I'll suggest you do the same. He explains the concepts in a beginner-friendly way.
I have 2 classes and I´d like to show a list of fields from the second class based on the first class. For example:
servico:
id desc_servico
1 teste1
2 teste2
itemservico:
id desc_itemservico servico
1 itemteste1 1
2 itemteste2 2
In this example, if I choose servico=1, the itemservico has to show me itemteste1. If I choose servico=2, the itemservico has to show itemteste2.
Models.py:
class servico(models.Model):
desc_servico = models.CharField('Descrição', max_length=50, default='', blank=False, null=False)
class itemservico(models.Model):
desc_itemservico = models.CharField('Descrição', max_length=50, default='', blank=False, null=False)
val_itemservico = models.DecimalField(max_digits=8, decimal_places=2)
servico = models.ForeignKey(servico, default='', blank=True, null=True) # Chave estrangeira da Classe Serviço
ind_selecionado = models.BooleanField(default=False)
forms.py:
class itemservicoForm(forms.ModelForm):
servico = forms.ModelChoiceField(queryset=servico.objects.all().order_by('desc_servico'), empty_label="Serviço")
class Meta:
model = itemservico
fields = (
'servico',
)
template.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ORÇAMENTO</title>
</head>
<body>
<h2>ORÇAMENTO</h2>
<form class=" bd-form-20 " action="" name="form-name" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<label class="bd-form-label" >Serviço </label>{{form.servico}}<br><br>
<p><h1>{{form.servivo.id}}</h1></p>
<div class=" bd-customhtml-29 bd-tagstyles bd-custom-table">
<div class="bd-container-inner bd-content-element">
<table border="1" rules="all" cellspacing="0" cellpadding="10">
<tbody>
<tr>
<th>Testar</th>
<th>Selecionar</th>
<th>ID Item Serviço</th>
<th>Item Serviço</th>
<th>Valor Serviço</th>
<th>Serviço</th>
</tr>
{% for item in instance_itens %}
<tr>
<td> <input type="checkbox" id="item.ind_selecionado"></td>
<td>{{ item.ind_selecionado }}</td>
<td>{{ item.id }}</td>
<td>{{ item.desc_itemservico }}</td>
<td>{{ item.val_itemservico }}</td>
<td>{{ item.servico_id}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</form>
<br><br>
<button class="bd-linkbutton-60 bd-button bd-own-margins bd-content-element" type = "submit" >
OK</button>
</body>
</html>
I think you need to use Ajax post for generating results based on selection.
for view.py part you need use this function:
def ajax_post(request):
if request.POST:
if request.POST.get('servico') and request.is_ajax():
itemservico_list = itemservico.objects.filter(servico = request.POST.get('servico'))
return render_to_response('itemservico.html',{'itemservico_list':itemservico_list})
For Html part you need to generate itemservico.html seperated and include it to main html such as
<form method="post">{% csrf_token %}
<tbody><tr>
<th>Testar</th>
<th>Selecionar</th>
<th>ID Item Serviço</th>
<th>Item Serviço</th>
<th>Valor Serviço</th>
<th id='targetselection'>Serviço</th>
</tr>
<div id='inner'>
{% include "itemservico.html" %}
</div>
</tbody>
</form>
And you need to create another html file for itemservico such as
{% for item in itemservico_list %}
<tr>
<td> <input type="checkbox" id="item.ind_selecionado"></td>
<td>{{ item.ind_selecionado }}</td>
<td>{{ item.id }}</td>
<td>{{ item.desc_itemservico }}</td>
<td>{{ item.val_itemservico }}</td>
<td>{{ item.servico_id}}</td>
</tr>
{% endfor %}
For JS part you need to create a js file for which detect state changes or key press of any given field: (I use jquery for selection)
$('#targetselection').on('change keyup paste', function() {
servico = $(this).val() // get the current value of the input field.
ajaxPost(servico);
});
function ajaxPost(query){
$.ajax({
type:"POST",
url:'',
data: {
csrfmiddlewaretoken:token,
servico : servico
},
dataType:'html',
success:function (data,textStatus,jqXHR){
$('#inner').html(data);
}
});
}
As a summary: When some selection changes on the web site, its creating an ajax request on server. As a view part you are filtering data according to ajax post request. Rendering html file and pushing it into specific part of web page. I hope this would be helpfull
I have a formset from a model, but when I try to save more than one formset I get the error:
KeyError at /auditoria/auditoria_tab/ 'id_control'
My view:
#staff_member_required
def AuditoriaView(request):
class RequiredFormset(BaseFormSet):
def __init__(self,*args,**kwargs):
super(RequiredFormset,self).__init__(*args,**kwargs)
for form in self.forms:
form.empty_permitted = False
auditoriaFormset = formset_factory(AuditoriaForm,max_num=31,formset=RequiredFormset)
if request.method == 'POST':
usuario = request.user
accion = 'Registro nuevo Detalle de Auditoria'
formset = auditoriaFormset(request.POST)
for form in formset.forms:
if formset.is_valid():
obj = form.save(commit=False)
#obj.id_control = control
obj.auditor = request.user
obj.save()
else:
formset = auditoriaFormset()
return render_to_response('audit_auditoria.html',
{'formset':formset},
context_instance=RequestContext(request))
My form:
class AuditoriaForm(forms.ModelForm):
id_control = forms.ModelChoiceField(queryset=Control.objects.all().order_by('nombre'),\
required=True,label='Control', widget=forms.Select(attrs={'class':'form-control'}))
id_analista = UserModelNameChoiceField(queryset=User.objects.filter(is_staff=False),\
required = True, label='Analista', widget=forms.Select(attrs={'class':'form-control'}))
fecha = forms.DateField(widget=DateInputCustom())
id_tipoerror = forms.ModelChoiceField(queryset=TipoError.objects.all(),required=True, label='Tipo Falla', widget=forms.Select(attrs={'class':'form-control'}))
id_sharepoint = forms.IntegerField(label='Sharepoint', widget=forms.NumberInput(attrs={'class':'form-control','style': 'width:80px'}))
id_estado = forms.ModelChoiceField(queryset=Estado.objects.all(),required=True, label='Estado', widget=forms.Select(attrs={'class':'form-control'}))
observaciones = forms.CharField(widget=forms.Textarea(attrs={'rows':5,'cols':30}))
class Meta:
model = DetalleAuditoria
exclude = ('auditor',)
def __init__(self,*args,**kwargs):
super(AuditoriaForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
def clean(self):
cleaned_data = super(AuditoriaForm,self).clean()
idc = cleaned_data['id_control']
fechac = cleaned_data['fecha']
try:
DetalleAuditoria.objects.get(id_control = idc,fecha = fechac )
raise forms.ValidationError('Esta Incidencia ya ha sido registrada')
except DetalleAuditoria.DoesNotExist:
pass
return cleaned_data
Template:
<form id="form_audit" class="form" method="post" action="/auditoria/auditoria_tab/">
<table class="table table-responsive table-condensed table-bordered">
{{ formset.management_form }}
{% for form in formset.forms %}
<thead>
<th class="info"colspan="6">
<label >{{ form.id_control.label|capfirst }} </label>
{{ form.id_control }}
</th>
</thead>
<thead>
<th>{{ form.id_analista.label|capfirst }}</th>
<th>{{ form.id_sharepoint.label|capfirst }}</th>
<th>{{ form.fecha.label|capfirst }}</th>
<th>{{ form.id_tipoerror.label|capfirst }}</th>
<th>{{ form.id_estado.label|capfirst }}</th>
<th>{{ form.observaciones.label|capfirst }}</th>
<thead>
<tbody>
<tr class="{% cycle row1,row2 %} formset_row">
<td>{{ form.id_analista }}</td>
<td>{{ form.id_sharepoint }}</td>
<td>{{ form.fecha }}</td>
<td>{{ form.id_tipoerror }}</td>
<td>{{ form.id_estado }}</td>
<td>{{ form.observaciones }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-info" value="Registrar">
</form>
{% block extra_js %}
<script src="{{STATIC_URL|default:"/static/"}}admin/js/jquery.formset.js"></script>
<script type="text/javascript">
function renewDatePickers() {
$('.datepicker').datepicker('destroy');
$('.datepicker').datepicker({
format: "yyyy-mm-dd",
startDate: "2014-01-01",
endDate: "2020-01-01",
autoclose: true
});
}
$(renewDatePickers);
$('.formset_row').formset({
addText: 'Agregar Detalle Auditoria',
deleteText: 'Eliminar',
prefix: '{{ formset.prefix }}',
added: renewDatePickers
});
</script>
{% endblock extra_js %}
how can I use the field ID_CONTROL from the first formset in the other formsets, when I want to save the data.
I am not sure if I am using in the right way the formsets?.
Any advice
Thanks in Advance
UPDATE:
I get the error in the "clean" function. When the view try to save the second formset ther error appeare in this line in the:
idc = cleaned_data['id_control']
In the error page I can see the post and I can see for the first formset the id_control value, but for the second formset I see all the fields except id_control.
It seems to me a non sense to put the same control id into the next forms in that way.
Anyway, to solve what you asking for, this should be valid (having different "fecha" value the forms):
id_control=None
if request.method == 'POST':
usuario = request.user
accion = 'Registro nuevo Detalle de Auditoria'
formset = auditoriaFormset(request.POST)
for counter, form in enumerate(formset.forms):
if formset.is_valid():
obj = form.save(commit=False)
if counter>0:
obj.id_control = id_control
obj.auditor = request.user
obj.save()
if counter==0:
id_control=obj.id_control
else:
formset = auditoriaFormset()