Django form post not saving - python

I am making a Django application (for the first time in my life). As part of the application, a timetable needs to be implemented. Loading data (from the databse) into the timetable works fine:
Timetable view
The thing is that the data should be editable. So users need to be able to change the time or the yes/no parameter. I've created a POST handler in views.py, but when I press save, the page quickly reloads and the old values are back. What am I doing wrong?
models.py
class timeTable(models.Model):
key = models.CharField(max_length=200, unique=True, null=True)
value = models.CharField(max_length=200, null=True)
def __str__(self):
return self.key
views.py
#login_required(login_url='login')
def timetable(request):
timeTableFormset = modelformset_factory(timeTable, fields='__all__' ,extra=0)
timetableform = timeTableFormset(queryset=timeTable.objects.all())
if request.method == 'POST':
form = timeTableFormset(request.POST)
if form.is_valid():
form.save()
return render(request, 'VulnManager/timetable.html', {'timetableform': timetableform})
timetable.html:
<form method="POST">
{% csrf_token %}
<table id="tablePreview" class="table table-borderless table-hover">
<!--Table head-->
<thead>
<tr>
<th></th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
<th>Sunday</th>
</tr>
</thead>
<!--Table head-->
<!--Table body-->
<tbody>
<tr>
<th scope="row">Scan?</th>
{{ timetableform.management_form }}
{% for timeTableValue in timetableform.forms %}
{% if forloop.counter <= 7 %}
<td><select class="form-control" id="{{ timeTableValue.key.value }}" disabled="true">
<option>{{ timeTableValue.value.value }}</option>
<option>
{% if timeTableValue.value.value == "true" %}
false
{% elif timeTableValue.value.value == "false" %}
true
{% endif %}
</option>
</select></td>
{% if forloop.counter == 7 %}
</tr>
<tr>
<th scope="row">Time begin</th>
{% endif %}
{% elif forloop.counter >= 8 and forloop.counter <= 14 %}
<td><input type="text" id="{{ timeTableValue.key.value }}" onchange="validateHhMm(this);" value="{{ timeTableValue.value.value }}" readonly /></td>
{% if forloop.counter == 14 %}
</tr>
<tr>
<th scope="row">Time end</th>
{% endif %}
{% elif forloop.counter >= 15 and forloop.counter <= 21 %}
<td><input type="text" id="{{ timeTableValue.key.value }}" onchange="validateHhMm(this);" value="{{ timeTableValue.value.value }}" readonly /></td>
{% endif %}
{% endfor %}
</tr>
</tbody>
<!--Table body-->
</table>
<input type="button" class="btn-primary" id="edit" value="Edit" />
<input type="submit" class="btn-primary" id="save" value="Save" />
</form>

Your "form" variable has the formset in it, but you have to loop through the forms in the formset individually:
if form.is_valid():
for form_line in form:
form_line.save()

Your view is always rendering the "get" form for lack of a better term.
I think if you keep your variable names the same, it'll make things a bit simpler and consistent. So to explain this;
views.py
#login_required(login_url='login')
def timetable(request):
timeTableFormset = modelformset_factory(timeTable, fields='__all__' ,extra=0)
timetableform = timeTableFormset(queryset=timeTable.objects.all())
if request.method == 'POST':
form = timeTableFormset(request.POST) # This form just gets left in this block
if form.is_valid():
form.save()
return render(
request,
'VulnManager/timetable.html',
{'timetableform': timetableform} # The context includes the fresh new form, with no post data
)
What you should do, is just call it formset, because it makes life easier;
from django.contrib import messages
#login_required(login_url='login')
def timetable(request):
timeTableFormset = modelformset_factory(timeTable, fields='__all__' ,extra=0)
formset = timeTableFormset(queryset=timeTable.objects.all())
if request.method == 'POST':
formset = timeTableFormset(data=request.POST, queryset=timeTable.objects.all())
if formset.is_valid():
formset.save()
# Do something here to reassure the user it saved. Redirect or add a message
messages.success(request, 'Form saved.')
return render(
request,
'VulnManager/timetable.html',
{'timetableform': formset}
)
There's an article here which may provide some worthy reading for you; https://medium.com/#MicroPyramid/understanding-djangos-model-fromsets-in-detail-and-their-advanced-usage-131dfe66853d

Related

How to validate an inline formset within a create view

I have an inline formset and 2 different forms within a create view of one of the forms.
All my fields in the model are blank=False and the inline formset has a minimum validation of 1.
If I try to send the form without any fields from the create view form, it displays the classic message "Please fill out this field" without rerendering the page. I want to achieve the same thing when the formset is not filled correctly. Also the same for the second form (this form is only displayed when the formset is filled based on the choice).
I am able to get the error messages re rendering the form but I'd like to do it before sending it to the server. Is it possible changing something in the model or I need to do it via js on the front?
Many thanks.
Views:
class SolicitacionReferenciaCreate(CreateView):
template_name = 'microcredito/solicitacion_form.html'
model = ContactoInfo
fields = ['nombre', 'destinoPrestamo', 'apellido']
success_url = reverse_lazy('accounts/login')
def get_context_data(self, **kwargs):
data = super(SolicitacionReferenciaCreate, self).get_context_data(**kwargs)
referencias_form_1 = ReferenciaForm()
data['referencias_form_1'] = referencias_form_1
if self.request.POST:
data['plataformas'] = IngresosFormSet(self.request.POST)
else:
data['plataformas'] = IngresosFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
plataformas = context['plataformas']
with transaction.atomic():
#ContactoInfo form
if plataformas.is_valid() and form.is_valid():
print("VALID")
self.object = form.save()
plataformas.instance = self.object
cuposPlataformas = []
for f in plataformas:
cd = f.cleaned_data
try:
score = ScorePersonas.objects.filter(idPlataforma=cd.get('idPlataforma'), numberPlataforma=cd.get('number_plataforma'),
aprobado=1)[0]
cuposPlataformas.append(score.cupo)
except:
try:
score = ScorePersonas.objects.filter(idPlataforma=cd.get('idPlataforma'), numberPlataforma=cd.get('number_plataforma'),
aprobado=0)[0]
cuposPlataformas.append(0)
except:
pass
plataformas.save()
self.object.save()
referenciaCreado1 = Referencia.objects.create_Referencia(self.object, plataformaObjeto, rt_referenciado_1, celular_referenciado_1,
nombre_referencia, apellido_referencia)
referenciaCreado1.save()
else:
#messages.error(request, "Error")
return self.render_to_response(self.get_context_data(form=form))
return render(self.request, 'microcredito/solicitacion_form.html', context)
Template:
<form action="" method="post" id="contactForm">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.source.errors }}
{{ form.source }}
{{ form.nombre.errors }}
{{ form.nombre|as_crispy_field }}
<table class="table">
{{ plataformas.management_form }}
{% for form in plataformas.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</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 }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<div class="form-row hide-element-form" id="ref_div_id_1">
<div class="form-group col-md-6 mb-0">
{{ referencias_form_1.numberPlataforma_referencia|as_crispy_field }}
</div>
</div>
<input onsubmit="formSent" id="form_button" type="submit" value="Save"/>
</form>
Formset:
IngresosFormSet = inlineformset_factory(ContactoInfo, IngresosPersonas,
form=IngresosForm, extra=1, min_num=1, max_num=8, validate_min=True)

Django fields editable in create but not in edit

I am creating a small application where I am able to add / modify / delete / view a member.
def member_edit(request,member_id):
MemberForm = modelform_factory(Member, fields=('employee_id', 'employee_name'))
if request.method == 'POST':
member = MemberForm(request.POST)
if member.is_valid():
member.save()
return HttpResponseRedirect(reverse("members:member_view"))
else:
member = get_object_or_404(Member, pk=member_id)
return render(request, 'members/member_edit.html', {'member': member})
I notice that instead of being able to edit all the fields become in display mode.
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<table>
<tr>
<th> <label for="{{ member.employee_id.id_for_label }}">Employee id:</label> </th>
<td> <input type="text" value = {{ member.employee_id }} /> </td>
<td> {{ member.employee_id.errors }} </td>
</tr>
<tr>
<th> <label for="{{ member.employee_name.id_for_label }}">Employee Name:</label> </th>
<td> <input type="text" value = {{member.employee_name }} /></td>
<td> {{ member.employee_name.errors }} </td>
</tr>
</table>
<input type="submit" value="Update">
If I change the member_edit function to this, it works but I don't have any data to change(obviously) :
def member_edit(request,member_id):
MemberForm = modelform_factory(Member, fields=('employee_id', 'employee_name'))
if request.method == 'POST':
member = MemberForm(request.POST)
if member.is_valid():
member.save()
return HttpResponseRedirect(reverse("members:member_view"))
else:
member = MemberForm() # change made only here
return render(request, 'members/member_edit.html', {'member': member})
What am I missing here ?
I think you need to pass the member instance to the form.
from django.shortcuts import get_object_or_404
def member_edit(request, member_id):
MemberForm = modelform_factory(Member, fields=('employee_id', 'employee_name'))
if request.method == 'POST':
instance = get_object_or_404(Member, id=member_id)
member = MemberForm(request.POST, instance=instance)
if member.is_valid():
member.save()
return HttpResponseRedirect(reverse("members:member_view"))
else:
member = MemberForm() # change made only here
return render(request, 'members/member_edit.html', {'member': member})

Django deletes previous entry when submiting new one

Title is pretty self-explanatory the thing is when I try to submit a new entry django deletes the previous one. I think that the problem is in the delete function because when I disable it I can sumbit as many entries as I want. Here are my files:
Form.py
class FinancesForm(forms.Form):
description = forms.CharField(max_length = 50)
value = forms.DecimalField(max_digits = 10, decimal_places = 2)
Models.py
class Finances(models.Model):
date = models.DateField(auto_now = True)
description = models.CharField(max_length = 50)
value = models.DecimalField(max_digits = 10, decimal_places = 2)
total = models.DecimalField(max_digits = 10, decimal_places = 2, null = True)
Views.py
#login_required
def finances(request):
if request.method == 'POST':
form = FinancesForm(request.POST)
if form.is_valid():
newEntry = Finances()
newEntry.description = form.cleaned_data['description']
newEntry.value = form.cleaned_data['value']
if Finances.objects.all().aggregate(Sum('value')).values()[0] == None:
newEntry.total = newEntry.value;
else:
newEntry.total = Finances.objects.all().aggregate(Sum('value')).values()[0] + newEntry.value
newEntry.save()
return HttpResponseRedirect(reverse('finances'))
form = Finances()
entries = Finances.objects.all()
total = Finances.objects.all().aggregate(Sum('value')).values()[0]
return render(request, 'coop/finances.html', {'entries': entries, 'form':form, 'total': total})
#login_required
def fdelete(request):
if request.method != 'POST':
raise HTTP404
entryId = request.POST.get('entry', None)
entToDel = get_object_or_404(Finances, pk = entryId)
entToDel.delete()
return HttpResponseRedirect(reverse('finances'))
Finances.html
<tbody>
{% if entries %}
{% for entry in entries %}
{% if forloop.last %}
<tr>
<td>{{ entry.date }}</td>
<td>{{ entry.description }}</td>
{% if entry.value >= 0 %}
<td class="positive">{{ entry.value }}</td>
{% else %}
<td class="negative">{{ entry.value}}</td>
{% endif %}
<td>{{entry.total}}</td>
<td>
<form action="{% url "fdelete" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="entry" value="{{ entry.pk }}"/>
<input class="btn btn-primary" type="submit" value="Eliminar"/>
</td>
</tr>
{% else %}
<tr>
<td>{{ entry.date }}</td>
<td>{{ entry.description }}</td>
{% if entry.value >= 0 %}
<td class="positive">{{ entry.value }}</td>
{% else %}
<td class="negative">{{ entry.value}}</td>
{% endif %}
<td>{{entry.total}}</td>
<td></td>
</tr>
{% endif %}
{% endfor %}
{% endif %}
<tr>
<td></td>
<form class="input-group" action="{% url "finances" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<td><input class="form-control" type="text" placeholder="Concepte" name="description"/></td>
<td><input class="form-control" type="text" placeholder="Quantitat €" name="value" onkeydown="if (event.keyCode == 13) { this.form.submit(); return false; }"/></td>
</form>
<td></td>
<td></td>
</tbody>
Thanks for yout time
You have not closed your first form
<td>
<form action="{% url "fdelete" %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="entry" value="{{ entry.pk }}"/>
<input class="btn btn-primary" type="submit" value="Eliminar"/>
</td>
Add closing </form> tag

Django ChoiceField

I'm trying to solve following issue:
I have a web page that can see only moderators.
Fields displayed on this page (after user have registered):
Username, First name+Last name, Email, Status, Relevance, etc.
I need to display table with information of all users stored in db with this fields, but two of fields have choices, so I want to make option that moderators can choose another option and after clicking on "Update" button this fields will be updated for selected user.
I can to display all choices of "status" and "relevance" fields, and after I choose new options from dropdown db is updated.
I want to display dropdowns and option stored in db should be selected.
I've tried a lot of options, I googled a lot of my time, and searched for answer or for right direction in StackOverFlow too, but didn't find anything.
Sorry for my bad english and thank you in advance for help!
Below is part of my code:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User)
status = models.IntegerField(choices=((1, _("Not relevant")),
(2, _("Review")),
(3, _("Maybe relevant")),
(4, _("Relevant")),
(5, _("Leading candidate"))),
default=1)
relevance = models.IntegerField(choices=((1, _("Unread")),
(2, _("Read"))),
default=1)
forms.py:
class CViewerForm(forms.Form):
status = forms.ChoiceField(label="",
initial='',
widget=forms.Select(),
required=True)
relevance = forms.ChoiceField(widget=forms.Select(),
required=True)
views.py:
#group_required('Managers')
#render_to('reader/view.html')
def admins_view(request):
users_list = Profile.objects.select_related('user').all()
users_dict = dict()
if request.method and request.method == 'POST':
form = CViewerForm(request.POST)
if form.is_valid():
d = form.cleaned_data
# get all selected choices
status_list = request.POST.getlist('status')
relevance_list = request.POST.getlist('relevance')
# get all usernames viewed on page
users_list = request.POST.getlist('username')
# create dict from all those lists
users_dict = zip([user for user in users_list], [status for status in status_list], [rel for rel in relevance_list])
# run through dict and do bulk update
for user_dict in users_dict:
Profile.objects.filter(user__username=user_dict[0]).update(status=user_dict[1], relevance=user_dict[2])
return HttpResponseRedirect(reverse('reader:admins_view'))
else:
form = CViewerForm()
return {'users_list': users_list,
'user': request.user,
'form': form}
template:
<form class="form-horizontal" action="" method="post" name="update-form" class="well form-inline" id="view_form">
{% csrf_token %}
{{ form.non_field_errors }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% if user.is_staff %}
<div>
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{% trans 'Username' %} </th>
<th>{% trans 'E-mail' %} </th>
<th>{% trans 'Status' %} </th>
<th>{% trans 'Relevance' %} </th>
</tr>
</thead>
<tbody>
{% for user in users_list %}
<tr>
<td><input type="text" READONLY name="username" value="{{ user.user.username }}"></td>
<td>{{ user.user.first_name }}</td>
<td>{{ user.user.last_name }}</td>
<td>{{ user.user.email }}</td>
<td>{{ user.get_status_display }}</td>
<td>{{ user.get_relevance_display }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
<br>
{% endif %}
<div class="form-actions">
<input type="hidden" name="_cmd_personal">
<input type="submit" class="btn btn-info" value="{% trans 'Update' %}" name="update" class="default">
</div>
</form>
Below is a solution:
forms.py (as #Liarez wrote).
template:
<form class="form-horizontal" action="" method="post" name="update-form" class="well form-inline" id="view_form">
{% csrf_token %}
{% if user.is_staff %}
{% if users_list %}
<div>
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{% trans 'Username' %} </th>
<th>{% trans 'First name' %} </th>
<th>{% trans 'Last name' %} </th>
<th>{% trans 'E-mail' %} </th>
<th>{% trans 'CV Status' %} </th>
<th>{% trans 'CV Relevance' %} </th>
</tr>
</thead>
<tbody>
{% for user in users_list %}
<tr>
<td><input type="text" READONLY name="username" value="{{ user.user.username }}"></td>
<td>{{ user.user.first_name }}</td>
<td>{{ user.user.last_name }}</td>
<td>{{ user.user.email }}</td>
<td>
<select name="cv_status">
{% for key, status in status_choices %}
{% ifequal user.get_cv_status_display status %}
<option value="{{ user.cv_status }}" selected>{{ user.get_cv_status_display }}</option>
{% else %}
<option value="{{ key }}">{{ status }}</option>
{% endifequal %}
{% endfor %}
</select>
</td>
<td>
<select name="cv_signal">
{% for key, signal in signal_choices %}
{% ifequal user.get_cv_signal_display signal %}
<option value="{{ user.cv_signal }}" selected>{{ user.get_cv_signal_display }}</option>
{% else %}
<option value="{{ key }}">{{ signal }}</option>
{% endifequal %}
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
<br>
{% endif %}
<div class="form-actions">
<input type="submit" class="btn btn-info" value="{% trans 'Update' %}" name="update" class="default">
</div>
First I recommend you as #ChrisHuang-Leaver suggested to define a new file with all the choices you need it there, like choices.py:
STATUS_CHOICES = (
(1, _("Not relevant")),
(2, _("Review")),
(3, _("Maybe relevant")),
(4, _("Relevant")),
(5, _("Leading candidate"))
)
RELEVANCE_CHOICES = (
(1, _("Unread")),
(2, _("Read"))
)
Now you need to import them on the models, so the code is easy to understand like this(models.py):
from myApp.choices import *
class Profile(models.Model):
user = models.OneToOneField(User)
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
relevance = models.IntegerField(choices=RELEVANCE_CHOICES, default=1)
And you have to import the choices in the forms.py too:
forms.py:
from myApp.choices import *
class CViewerForm(forms.Form):
status = forms.ChoiceField(choices = STATUS_CHOICES, label="", initial='', widget=forms.Select(), required=True)
relevance = forms.ChoiceField(choices = RELEVANCE_CHOICES, required=True)
Anyway you have an issue with your template, because you're not using any {{form.field}}, you generate a table but there is no inputs only hidden_fields.
When the user is staff you should generate as many input fields as users you can manage. I think django form is not the best solution for your situation.
I think it will be better for you to use html form, so you can generate as many inputs using the boucle: {% for user in users_list %} and you generate input with an ID related to the user, and you can manage all of them in the view.
Better Way to Provide Choice inside a django Model :
from django.db import models
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
GRADUATE = 'GR'
YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
(GRADUATE, 'Graduate'),
]
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
New method in Django 3
you can use Field.choices Enumeration Types new update in django3 like this :
from django.db import models
class Status(models.TextChoices):
UNPUBLISHED = 'UN', 'Unpublished'
PUBLISHED = 'PB', 'Published'
class Book(models.Model):
status = models.CharField(
max_length=2,
choices=Status.choices,
default=Status.UNPUBLISHED,
)
django docs
If your choices are not pre-decided or they are coming from some other source, you can generate them in your view and pass it to the form .
Example:
views.py:
def my_view(request, interview_pk):
interview = Interview.objects.get(pk=interview_pk)
all_rounds = interview.round_set.order_by('created_at')
all_round_names = [rnd.name for rnd in all_rounds]
form = forms.AddRatingForRound(all_round_names)
return render(request, 'add_rating.html', {'form': form, 'interview': interview, 'rounds': all_rounds})
forms.py
class AddRatingForRound(forms.ModelForm):
def __init__(self, round_list, *args, **kwargs):
super(AddRatingForRound, self).__init__(*args, **kwargs)
self.fields['name'] = forms.ChoiceField(choices=tuple([(name, name) for name in round_list]))
class Meta:
model = models.RatingSheet
fields = ('name', )
template:
<form method="post">
{% csrf_token %}
{% if interview %}
{{ interview }}
{% endif %}
{% if rounds %}
<hr>
{{ form.as_p }}
<input type="submit" value="Submit" />
{% else %}
<h3>No rounds found</h3>
{% endif %}
</form>

Multiple fields on the same row in Django

I'd like to display a row with a label, a textfield and a checkbox for each item in a database. I've managed to do this except for the checkbox that's on a new line. I wan't:
<tr>
<td>Label</td>
<td>Input</td>
<td>Checkbox</td>
<tr>
But all I get is:
<tr>
<td>Label</td>
<td>Input</td>
</tr>
<tr>
<td>Checkbox</td>
</tr>
Anyone knows how to do this?
EDIT:
To generate the form I do:
forms.py
class AttributeForm(forms.Form):
def __init__(self, *args, **kwargs):
extra = kwargs.pop('extra')
super(AttributeForm, self).__init__(*args, **kwargs)
for key in extra:
self.fields[key] = forms.CharField(label=key, initial=extra[key], required=False)
self.fields['delete_'+key] = forms.BooleanField(label='', required=False)
views.py
attribute_form = AttributeForm(extra=user)
return render_to_response('user.html', {'username': username, 'attribute_form': attribute_form})
template (user.html)
<form action="" method="post">
<table>{{ attribute_form.as_table }}</table>
<input type="submit" value="Save attributes">
</form>
EDIT 2:
My template ended up like this:
<form action="" method="post">
<table>
<tr>
{% for field in attribute_form %}
{% cycle '<th>' '' %}{% cycle field.label_tag '' %}{% cycle '</th>' '' %}
<td>{{ field }}{{ attribute_form.field.errors }}</td>
{% if not forloop.last %}{% cycle '' '</tr><tr>' %}{% endif %}
{% endfor %}
</tr>
</table>
<input type="submit" value="Save attributes">
</form>
.as_table renders each form field in seperate table row. You should render the form manually.

Categories

Resources