Django Updateview not saving when in html but saves in admin - django

I have a big form, some fields, depends on other fields value to compute.
Since so, i used jquery blur to submit the form each time user done editing a field and redirect it to updateview again with new values.
I have this day_absent and amount absent to test this.
If i just put form.day_absent in the form and put object.amt_absent, it doesnt save the form. But if i put form.amt_absent, it saves the form.
Why is that? I do not wish to put amt_absent as editable, it should just be the day_absent.
class PayrollTransactionUpdate(LoginRequiredMixin,UpdateView):
model = t_pay
template_name = 'payroll/transaction/update.html'
fields = [
'day_absent',
'amt_absent',
]
<div id='input-item' class="col-1">
{{ form.day_absent }}
{{ form.day_absent.errors}}
</div>
<div id='item' class="col-2">
{{ object.amt_absent }}
</div>

Couple of points:
When you use {{object.amt_absent}}, you are not rendering the form for the field amt_absent, rather just the value saved in the DB.
You added amt_absent as a field to be included in the UpdateView, so, until and unless this field is not required, the form will keep throwing error for this field. That is why your form works when you include both the fields. You can check all errors by using {{form.errors}} or field specified errors by using {{form.amt_absent.errors}} in your template.

Related

Crispy form: render a field as HTML

I've a form that contains various fields, some of them should be read-only, but instead of using a "read-only" field i would like to rednder them as HTML.
For examples i want:
<p>{{form.text.value}}</p> to be used. so that it's rendered as pure html and not as an input.
there's the HTML tag that works as a template, but I can't figure out how (and if) i can use it.
Assuming that my field is called text, what should I use to render it?
HTML("This is the text {{text}}") this does not work :(
Or should I create a new template for a new Field in oreder to render it as I want?
I didn't use crispy form before but I try to help you. In a regular form, You can use disabled=True in your related form field. It automatically adds read-only feature to the template and an additional validation to avoid from changing the field.
According to docs:
The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. Even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data.
So it will be much safer for your form.
An example:
class ExampleForm(forms.Form):
readonly_field = forms.CharField(disabled = True)
Then in your HTML template:
<form method = "POST">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit">
</form>
I guess you can handle with adding extra crispy-form template tags.

Can you get the django form id from django template using id_fieldname?

I am using a model form that has 3 fields - name, country and city. My template looks like this-
<form method="post" id="personForm" data-cities-url="{% url 'ajax_load_cities' %}">
{% csrf_token %}
{{form}}
<input type="submit" value="Submit">
</form>
Following this is a bit more jQuery code that fetches the id for the "country" field using a code like this -
$("#id_country").change(function ()....
Two things I need to mention here, no where in my code I have id_country except from this jQuery code. Also I haven't set the id attribute for the country field anywhere.
So my question is , is this a thing in django or in jQuery where you can get the id for a field using id_ in front of the field name?
I read the django documentation, haven't seen anything like this.
What am I missing here?
Thanks for any input.
When one renders a Django form, it automatically renders an id attribute on the form fields. As you noticed this id is of the form id_<field-name>. This is noted in the documentation in the section Working with form templates:
Note that each form field has an ID attribute set to id_<field-name>
Hence you have an id as id_country because you have a form field named country. If you were to have a field named foo its id would be id_foo.

In a forms of an inlineformset how can I know which are my default form fields and which are the ones added by Django?

I have a specific formset (inlineformset), and I want to make some customization regarding the default design.
I loop thru it this way:
{% for form in formset %}
<div class="ct-formset">
{% if form.errors %}<div>{{ form.errors }}</div>{% endif %}
{% for field in form %}{{ field }}{% endfor %}
{% endfor %}
For each form Django add two other fields the Foreign key field and the Delete Field.
Because I want to use the same code for multiple formsets, In the loop I don't request the field by name
I need to know in the:
{% for field in form %}{{ field }}{% endfor %}
How can I know which are my default form fields and which are the ones added by Django ?
While working with the form instance, you can't tell for sure what fields are added originally in the class or after instantiation.
Though, here you have some approaches on how to prevent those fields you don't want to be rendered in the resulting formset.
If you have access to the form's class ...
... at the moment you need to "filter" which fields where the original fields of such form you could:
>>> class MyForm(forms.Form):
>>> title = forms.CharField()
>>> class_dict = vars(MyForm)
>>> class_dict['declared_fields']
OrderedDict([('title', <django.forms.fields.CharField at 0x7f496ce067d0>)])
About *-DELETE and *-ORDER fields
When dealing with formsets, you must to have into account that not only Django can add extra fields to your forms, you also can do that.
First, we have to understand why these fields are there.
The case for the *-DELETE and *-ORDER fields are added just if you enable them using:
formset = formset_factory(..., can_delete=True, can_order=True)
Django uses BaseFormSet.add_fields in order to add that extra fields it needs to enable ordering or deletion, you could use it to add your own additional fields too.
What can you do?
You can just pass False there.
An interesting experiment ...
I perform an experiment in order to illustrate the effect overwritting this can have:
from django import forms
class MyForm(forms.Form):
title = forms.CharField()
class MyBaseFormSet(forms.BaseFormSet):
def add_fields(self, form, i):
# Avoiding FormSets using this to add additional fields
return form
# Create a form set with deletion and ordering enabled.
# pay attention to the argument: formset=MyBaseFormSet
MyFormSet = forms.formset_factory(MyForm, formset=MyBaseFormSet, can_delete=True, can_order=True)
fs = MyFormSet()
for form in fs:
print(form.as_table())
The result, no DELETE or ORDER fields added to the resulting forms.
>>> <tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
What about ForeignKey
That's something you can solve in the forms. If you don't want ForeignKey to be displayed, you could use forms.HiddenInput widget for such fields in your forms.
Also if you're working with ModelForms you can select the fields to use for generating the form.
Conclusion
With this info, I hope you to be able to plan how to implement your requirement of having a generic template for those formsets.
Starting points:
Pass can_delete or/and start_order as False to formset_factory.
Set forms.HiddenInput widget for ForeignKey fields in your forms.
Find a way to get the form class and use vars in order to find out the
original fields of the form.
If you're using ModelForms, use the fields or exclude meta configuration in order to state which fields are used to build the form.

how to differentiate between already created and new objects in the template while using model formset

I am not quite sure about if i am taking the right approach to accomplish this. What i want is to (change the css for) / (use some jQuery methods on) the form elements rendered by the modelformset that have pre-filled data. For example i have the following modelformset.
EduFormSet = modelformset_factory(models.CandidateDegree,
form=forms.CandidateDegreeForm)
edu_formset = EduFormSet(prefix='candidate_degree',
queryset=models.CandidateDegree.objects\
.filter(candidate=can))
when i pass this formset to the template it renders forms for all the existing CandidateDegree objects with the pre-filled data and a blank form as well.
What i am trying to achieve is not to show the pre-filled forms but just the data for the objects that are already created and append an edit button to the element (using jQuery) which would then show the form. And at the end of the object list show the blank form generated by formset.
i am aware that i could pass all the CandidateDegree objects related to a Candidate as a seperate dictionary to show the information. but in that case how do i append the form to the formset so that the dynamically generated forms become the part of the formset and all the information is saved when a user clicks on a submit button.
what would be the best approach to achieve something like this ?
You don't have to show the complete form, you can loop over the formset like this:
{% for form in edu_formset %}
form.FIELDNAME#1.value
form.FIELDNAME#2.value
etc.
{% endfor %}
<!-- Manually render empty form for new entry -->
<div class='input'>
<label>Locality: </label> <input id="FIELDNAME#1" disabled=disabled value="">
<label>Country: </label> <input id="FIELDNAME#2" disabled=disabled value="">
etc.
</div>
So you're only showing the database values of all objects send to the template, not the form inputs.
Then indeed with minimal jquery (or plain javascript) you can show/hide the div.input

How to get the order value of a form within template (django)?

I am using formset for my project. I have several form in my formset. Now I want to customize the appearance of form. I want to do this by using the order value of each form. One example of the input for an "ORDER" of form is shown below:
<input type="text" name="phones-0-ORDER" value="1" id="id_phones-0-ORDER">
I want to get the value(value="1" in this case) of this input.
I have generated the formset from my models directly using the inlineformset_factory in my view.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-an-inline-formset-in-a-view
At the creation of my formset, I have used the following code:
PhoneNumberFormSet = inlineformset_factory(Patron, PhoneNumber, can_order=True)
In this way, every form in the formset will have an order. Let's say we have 3 forms in the formset, the first form will hold the order 1, the second order 2, the third order 3.
I want to use the "order" of the form in my template to control a loop.
Anyone knows how to get the order value in template?
For your information, the type of ORDER is IntegerField. So my question is equal to "how to get the initial(pre filled-in) data of an IntegerField in template.
Thanks for your answers!
I believe you are asking how to set initial data within a formset. If that is the case, you will find the following information valuable within the django docs:
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-initial-data-with-a-formset
If you are actually asking about how to get to the values from the client side, you would want to do so with jquery selectors.
http://api.jquery.com/category/selectors/
{% if form.can_order %}
{{ form.ORDER }}
{% endif %}