Required field error happans only when using ajax - django

I have a model named SaleEntry:
class SaleEntry(models.Model):
date = models.DateField()
ebay_price = models.FloatField()
amazon_price = models.FloatField()
ebay_tax = models.FloatField()
paypal_tax = models.FloatField()
tm_fee = models.FloatField(default=0.3)
promoted = models.FloatField(default=0.0)
profit = models.FloatField()
discount = models.FloatField(default=0)
country = models.CharField(max_length=100, default="-----")
user = models.ForeignKey(User, on_delete=models.CASCADE, default=0)
def save(self, *args, **kwargs):
if not self.pk: # object is being created, thus no primary key field yet
change_balance = Balance.objects.get(user=self.user)
change_balance.balance = change_balance.balance - self.amazon_price - self.tm_fee + self.discount
change_balance.save()
super(SaleEntry, self).save(*args, **kwargs)
def calc_profit(self):
return self.ebay_price - self.amazon_price - self.ebay_tax - self.paypal_tax - self.tm_fee - self.promoted + self.discount
def __str__(self):
return f'{self.user} - {self.profit}'
And I have a form handling this model SaleEntryForm:
class SaleEntryForm(ModelForm):
class Meta:
model = SaleEntry
fields = "__all__"
widgets = {
'date': DateInput(attrs={'class': 'form-control', 'id':'f_date'}),
'ebay_price': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'eBay Price', 'id':'f_ebay_price', 'onkeyup': 'calc_profit()'}),
'amazon_price': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Amazon Price', 'id':'f_amazon_price', 'onkeyup': 'calc_profit()'}),
'ebay_tax': forms.NumberInput(attrs={'class': 'form-control col-1', 'placeholder': 'eBay Tax', 'id':'f_ebay_tax', 'onkeyup': 'calc_profit()'}),
'paypal_tax': forms.NumberInput(attrs={'class': 'form-control col-1', 'placeholder': 'Paypal Tax', 'id':'f_paypal_tax', 'onkeyup': 'calc_profit()'}),
'tm_fee': forms.NumberInput(attrs={'class': 'form-control col-1', 'placeholder': 'TM Fee', 'id':'f_tm_fee', 'onkeyup': 'calc_profit()'}),
'promoted': forms.NumberInput(attrs={'class': 'form-control col-1', 'placeholder': 'Promoted', 'id':'f_promoted', 'onkeyup': 'calc_profit()'}),
'profit': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Profit', 'readonly':'true', 'id':'f_profit'}),
'discount': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': 'Discount', 'id':'f_discount'}),
'country': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Country', 'id':'f_country'}),
}
def __init__(self, *args, **kwargs):
'''
relate the sale registration form to the user who created it.
'''
user_id = kwargs.pop('user_id')
super().__init__(*args, **kwargs)
self.fields['user'] = forms.ModelChoiceField(queryset=User.objects.filter(id=user_id), empty_label=None, initial=User.objects.get(id=user_id))
self.fields['user'].widget.attrs['class'] = 'no-display'
I am using this form in the html page:
<form id="form_add_sale">
{% csrf_token %}
<tr>
{% for field in form %}
{% if field is not form.user %}
<td>
{{ field }}
</td>
{% else %}
{{ field }}
{% endif %}
{% endfor %}
<td><input class="btn btn-primary" type="submit" display="inline" name="btn_register_sale"></td>
</tr>
</form>
and this is the ajax to send the data to server:
$(document).on("submit", '#form_add_sale', function(e){
e.preventDefault();
$.ajax({
url:"{% url 'add_sale' %}",
type:"POST",
data:{
date: $("#f_date").val(),
ebay_price: $("#f_ebay_price").val(),
amazon_price: $("#f_amazon_price").val(),
ebay_tax: $("#f_ebay_tax").val(),
paypal_tax: $("#f_paypal_tax").val(),
tm_fee: $("#f_tm_fee").val(),
promoted: $("#f_promoted").val(),
profit: $("#f_profit").val(),
discount: $("#f_discount").val(),
country: $("#f_country").val(),
},
success: function(){
alert("Created new sale!");
}
})
//.done(function(response){
// $("#table_sales").load(location.href + " #table_sales");
//})
.fail(function(xhr, status, error){
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
})
})
upon submitting the form, I'm getting the next error (which didn't occur when submitting the form regularly without ajax with the exact same code in the view):
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
this is the request.POST:
<QueryDict: {'date': ['2021-02-11'], 'ebay_price': ['70'], 'amazon_price': ['50'], 'ebay_tax': ['10'], 'paypal_tax': ['5'], 'tm_fee': ['0.3'], 'promoted': ['0.0'], 'profit': ['4.70'], 'discount': ['0'], 'country': ['-----']}>

I think you forgot the csrf_token. try adding:
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
to the data.
More information can be found here

Related

How do I change input type of form field in django forms?

I have a form class in my form.py file where I initiated properties of form fields. I want to change my 'Description' field from an input field to a textarea field of height 5 lines. Also want to change my 'Deadline' field to a date input field because at the moment it's an input text field.
class JobPost_form(ModelForm):
class Meta:
model = Job_post
fields = "__all__"
def __init__(self, *args, **kwargs):
self.fields['Job_role'].widget.attrs.update(
{'class': 'form-control Password2', 'id': 'form3Example1c', 'placeholder': 'Job role'})
self.fields['Company'].widget.attrs.update(
{'class': 'form-control', 'id': 'form3Example1c', 'placeholder': 'Company'})
self.fields['Deadline'].widget.attrs.update(
{'class': 'form-control', 'id': 'form3Example1c', 'placeholder': 'YYYY-MM-DD HH:MM:SS'})
self.fields['Description'].widget.attrs.update(
{'class': 'form-control', 'id': 'form3Example1c', 'placeholder': 'Description'})
I think that in the code you have passed a tabulation is missing since the meta and the init will be inside the class: JobPost_form. As for marking the description field as a textarea you should do:
class JobPost_form(ModelForm):
description = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))
class Meta:
model = Job_post
fields = "__all__"
def __init__(self, *args, **kwargs):
...
You can achieve those things using wigets(attrs="{}") which provided by django in forms api
models.py
class DemoClass(models.Model):
description = models.TextField()
deadline = models.DateField()
form.py
class DemoForm(forms.ModelForm):
class Meta:
model = DemoClass
fields = "__all__"
widgets={
'description':forms.Textarea(attrs={"rows":"4", "cols":"50"}),
'deadline':forms.TextInput(attrs={'type':'date'}),
--------------- OR -------------------------------
'deadline':forms.DateInput(attrs={'type':'date'}),
}
HTML
{% block body %}
<div class="container">
<div class="row">
<div class="col-lg-12">
{{form.as_p}}
</div>
</div>
</div>
{% endblock body %}
Output

DateTimeField not shown in a template - django

I have two DateTime Fields in a model:
models.py
start_appointment = models.DateTimeField(default=timezone.now, blank=True)
end_appointment = models.DateTimeField(default=timezone.now, blank=True)
i also have a form where i set widgets for above fields:
'start_appointment': forms.DateTimeInput(attrs={'class': 'form-control', 'type': "datetime-local"}),
'end_appointment': forms.DateTimeInput(attrs={'class': 'form-control', 'type': "datetime-local"}),
i have an update view where i want to update appointment's fields for example start_appointment. However when rendering form in a template these two fields are shown as dd/mm/yyyy --:--, -- meaning values from database not shown, while all the others are rendered with no problem.
From the other hand i can execute the form with no problem and update is successful.
template:
<div class="form-group row">
<label class="col-form-label col-3 text-lg-right text-left">{% trans 'Start Appointment' %}</label>
<div class="col-9">
{{ form.start_appointment }}
</div>
</div>
Update
Adding forms.py
class AddAppointmentForm(forms.ModelForm):
class Meta:
model = Appointment
fields = ['user', 'name', 'appointment_notes', 'seat', 'start_appointment', 'end_appointment']
widgets = {
'user': forms.Select(attrs={'class': 'form-control'}),
'name': forms.TextInput(attrs={'class': 'form-control'}),
'appointment_notes': forms.Textarea(attrs={'maxlength': '900', 'class': 'form-control' }),
'seat': forms.Select(attrs={'class': 'form-control'}),
'start_appointment': forms.DateTimeInput(attrs={'class': 'form-control', 'type': "datetime-local"}),
'end_appointment': forms.DateTimeInput(attrs={'class': 'form-control', 'type': "datetime-local"}),
}
What might be the problem?
the problem might be this 'type': "datetime-local"
try this
try to change your datetime to something like this
'start_appointment': forms.DateTimeInput(format='%Y-%m-%d %H:%M:%S', attrs={'class':'datetimefield'})
and for datetimepicker you can use something like this
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
window.addEventListener("DOMContentLoaded", function () {
flatpickr(".datetimefield", {
enableTime: true,
enableSeconds: true,
dateFormat: "Y-m-d H:i:S",
});
});
</script>
you can learn more on Flatpickr here https://flatpickr.js.org/

DateTimeField not showing previous date and time when editing

I want previously set dates and times to be displayed before they are edited. The previously set name and description of my event are being displayed as it should. Here's a part of my code:
Forms:
class EventForm(forms.ModelForm):
name = forms.CharField(label='Event name', widget=forms.TextInput(attrs={'class': 'form-control'}))
description = forms.CharField(label='Description', required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4, 'cols': 15}))
date_hour_start = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'], widget=forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control col-md-4'}))
date_hour_end = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'], widget=forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control col-md-4'}))
Views:
def event_update_view(request, event_id):
event = get_object_or_404(Event, event_id=event_id)
event.date_hour_start = datetime.datetime.strftime(event.date_hour_start, '%d/%m/%Y %H:%M:%S')
event.date_hour_end = datetime.datetime.strftime(event.date_hour_end, '%d/%m/%Y %H:%M:%S')
if request.method == 'POST':
form = EventForm(request.POST, instance=event)
if form.is_valid():
event = form.save()
event.save()
return redirect(reverse('list_events'))
return redirect(reverse('list_events'))
def event_create_view(request):
form = EventForm(request.POST or None)
if form.is_valid():
form.save()
form = EventForm()
return redirect(reverse('list_events'))
context = {
'form': form
}
return render(request, "eventss/add_event.html", context)
HTML (just a part):
<div class="col-md-12">
<p style="margin-bottom: .5rem">Date and Time (Start) <span style="color:red">*</span></p>
{{ form.date_hour_start }}
</div>
<div class="col-md-12">
<p style="margin-bottom: .5rem">Date and Time (End)<span style="color:red">*</span></p>
{{ form.date_hour_end }}
</div>
Why isn't it working? Here's how it's showing. (It's aaaa because it's in Portuguese but it's the year)
To get the instance date and time to show in the datetime-locale input, please change the form input using widgets to the following:
widgets = {
'start_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'),
'end_date': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}, format='%Y-%m-%dT%H:%M'),
}

POST request not working for Django form and Django formset

I have a form, ApplyJobForm and a Formset, ApplyJobFormset. GET method works when I pass the form and the formset to a view, but for the post request the form and the formset is_valid() isn't working, after clicking submit it returns me to a view without saving. I am unable to save the form with the formset, I don't know what I'm doing wrong here.
Here are my codes.
models.py
class Applicants(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='applicants')
experience = models.IntegerField(blank=True, null=True)
cv = models.FileField(upload_to=user_directory_path)
degree = models.CharField(choices=DEGREE_TYPE, blank=True, max_length=10)
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return f'{self.user.get_full_name()} Applied'
class Certification(models.Model):
applicant = models.ForeignKey(Applicants, on_delete=models.CASCADE, related_name='applicant_certifications')
name = models.CharField(max_length=50)
certification = models.FileField(upload_to=user_directory_path, blank=True)
def __str__(self):
return f'{self.user.get_full_name()} certificate'
forms.py
class ApplyJobForm(forms.ModelForm):
class Meta:
model = Applicants
fields = ('job', 'degree', 'experience', 'cv')
exclude = ('job',)
labels = {
'degree': 'Degree',
'experience': 'Experience',
'cv': 'CV',
}
widgets = {
'degree': forms.Select(attrs={
'class': 'form-control',
}
),
'experience': forms.NumberInput(
attrs={
'class': 'form-control',
}
),
'cv': forms.FileInput(
attrs={
'class': 'form-control',
}
),
}
ApplyFormset = modelformset_factory(
Certification,
fields=('name', 'certification'),
extra=1,
widgets={
'name': forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': 'Certification name'
}
),
'certification': forms.FileInput(
attrs={
'class': 'form-control',
'placeholder': 'Upload certification'
}
)
}
)
views.py
def job_apply(request, job_id=None):
template_name = 'apply_form.html'
applyform = ApplyJobForm(request.GET or None)
job = get_object_or_404(Job, id=job_id)
formset = ApplyFormset(queryset=Certification.objects.none())
if request.method == 'GET':
context = {'applyform': applyform, 'formset': formset}
return render(request, template_name, context)
elif request.method == 'POST':
applyform = ApplyJobForm(request.POST)
formset = ApplyFormset(request.POST)
if applyform.is_valid() and formset.is_valid():
apply = applyform.save(commit=False)
applyform.job = job
apply.save()
for form in formset:
# so that `apply` instance can be attached.
certification = form.save(commit=False)
certification.apply = apply
certification.save()
return redirect('successful-apply')
else:
return redirect('job-detail', id=job.id)
return render(request, template_name, {'applyform': applyform, 'formset': formset})
Here an applicant can add as many certification field when applying for a job, although the certification field is not a required field. Certification model is bound to the Applicants model.
.html
<form class="form" method="POST" action="" enctype="multipart/form-data" role="form" autocomplete="off">
.................
</form>
First of all, never redirect if your forms are not valid. You want to render your template with the invalid form so that you can display the errors to the user. This also helps debugging since you'll see the errors.
So in your view, remove these two lines:
else:
return redirect('job-detail', id=job.id)
so that the invalid case renders the forms in your template.
Next, since you have files to upload, you need to initialise forms that require files with request.FILES:
formset = ApplyFormset(request.POST, request.FILES)
(and the same for applyform).
Finally make sure that in your template you are also displaying all the errors, either on each field ({{ form.<field>.errors }}) or globally ({{ form.errors }}).

django ImageField empty after form post

When I upload a photo to django through form the image field is empty. I confirmed this by using logging.info(form.cleaned_data['picture'] )
I checked through the console that the form has 'multipart/form-data'
The model :
class Person(models.Model):
user = models.ForeignKey(User, default=1)
name = models.CharField(max_length=250,null=True)
last_name = models.CharField(max_length=500)
treatment_plan=models.CharField(max_length=256,null=True,blank=True)
treatment_done=models.CharField(max_length=256,null=True,blank=True)
picture = models.ImageField(upload_to='image', storage=DEFAULT_FILE_STORAGE,blank=True)
def __str__(self):
return self.name
the modelForm :
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ['name', 'last_name', 'age', 'martial_status', 'mobile', 'sex',
'amount_paid','amount_left','note', 'address','date','picture','treatment_done','treatment_plan','chief_complain']
widgets = {
'name': forms.TextInput(attrs={'required': True, 'class': 'form-control',
'placeholder': 'name'}),
'last_name': forms.TextInput(attrs={'required': True, 'class': 'form-control',
'picture': forms.FileInput(attrs={'required': False,'class': 'form-control','enctype': 'multipart/form-data'}),
view.py :
def add_person(request):
if not request.user.is_authenticated():
return render(request, 'core/login.html')
else:
form = PersonForm(request.POST or None,request.FILES or None,instance=Person())
#form = PersonForm(request.POST or None,request.FILES )
if form.is_valid():
persons = form.save()
persons.user = request.user
#to capitalized the first letter so we have consistency when querying , this is a workaround since __iexact is not working
persons.name=persons.name.title()
persons.last_name=persons.last_name.title()
logging.info(form.cleaned_data['last_name'] )
persons.save()
return redirect('home')
context = {
"form": form,
}
return render(request, 'core/add_person.html', context)
The html template :
<div class="col-sm-4 form-group">
<label for="address">{% trans "Address" %}</label>
{{ form.address|add_class:"form-control" }}
</div>
<div class="col-sm-4 form-group">
<label for="pic">{% trans "Picture" %}</label>
{{form.picture}}
</div>
I may be wrong, but still, as far as I know the parameter "multipart/form-data" should be in the form, and not in input, and you install it to the FieldFile
'picture': forms.FileInput(attrs={'required': False,'class': 'form-control','enctype': 'multipart/form-data'}),