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'}),
Related
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
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'),
}
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 }}).
new in the field and struggling with a form validation in practice. I have created a form which i use as data input for a search in the DB. Form validation is only being triggered for the first field while the others seems not to be taken in consideration, bellow the code:
Form description:
class SearchForm(forms.Form):
cnp_nbr = forms.IntegerField(label='CNP', widget=forms.TextInput(attrs={'class': 'form-control' }), required=False)
first_name = forms.CharField(label='First Name', widget=forms.TextInput(attrs={'class': 'form-control'}), required=False)
last_name = forms.CharField(label='Last Name', widget=forms.TextInput(attrs={'class': 'form-control'}), required=False)
class Meta():
model = Clients
fields = ('cnp_nbr','first_name','last_name')
def clean(self): # most used!!!
all_clean_data = super().clean()
cnp_nbr = all_clean_data['cnp_nbr']
first_name = all_clean_data['first_name']
last_name = all_clean_data['last_name']
if cnp_nbr is None or (first_name is None and last_name is None):
raise forms.ValidationError("Enter f1 or f2&f3")
super().clean()
Views:
class ClientsSearch(generic.FormView):
form_class = forms.SearchForm
template_name = "administration/clients_search.html"
success_url = reverse_lazy('administration:searchresults')
def form_valid(self, form):
self.request.session['cnp_nbr'] = form.cleaned_data['cnp_nbr']
self.request.session['first_name'] = form.cleaned_data['first_name']
self.request.session['last_name'] = form.cleaned_data['last_name']
return super().form_valid(form)
class SearchResults (generic.ListView):
model = models.Clients
template_name='administration/search_results.html'
context_object_name = 'all_search_results'
def get_queryset(self):
return self.model.objects.filter(
Q(cnp_nbr__exact=self.request.session['cnp_nbr']) | Q(first_name__exact=self.request.session['first_name']) & Q(last_name__exact=self.request.session['last_name'])
)
HTML for search form:
<form method="POST" >
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary" id="search_submit" name = "search_submit" type="submit" > Search</button>
</form>
Validation is working only for cnp_nbr, i even tested them one by one.
This works :
if cnp_nbr is None:
raise forms.ValidationError("Enter field")
This doesnt
if first_name is None:
raise forms.ValidationError("Enter field")
Search is working just fine! Thanks in advance
That's because a not required IntegerField will be None if it's empty, whereas a not required CharField will be '' (the empty string) if it's empty. So you should not check last_name is None but just not last_name:
if cnp_nbr is None or not (first_name or last_name):
Note: Also I'd advise you to never directly assume the keys are present in the form's cleaned_data: Do first_name = all_clean_data.get('first_name') instead.
Note2: Why don't you just make cnp_nbr required since your ValidationError will trigger in any case if cnp_nbr isn't filled?
I'm using Django. I need a form that shows the 3 fields of a class that I have created, the idea is that every time you want to add a new day and time to start and end, show a new new section, so that each teacher can be found in more of a day and time of beginning and end (the three fields go together)
I still do not logo to make it work, if someone has any idea they would be grateful.
Models.py
class Profesor(Person):
legajo = models.IntegerField(blank=True, null=True)
type = models.CharField(max_length=30)
matter = models.CharField(max_length=100, blank=True, null=True)
calendar = models.ForeignKey('calendar', on_delete=models.DO_NOTHING)
user = models.CharField(max_length=20, blank=True, null=True)
class calendar(models.Model):
day = models.DateTimeField(default=date.today().isoweekday())
hs_init = models.DateTimeField(default=datetime.now().hour)
hs_end = models.DateTimeField(default=datetime.now().hour)
Forms.py
class addProfesorForm(ModelForm):
calendar = forms.ModelChoiceField(queryset=calendar.objects.all(), widget=forms.HiddenInput())
class Meta:
model = Profesor
TYPES = (
('Motiv', 'Motiv'),
('Academic', 'Académic'),
('Otro', 'Otro')
)
help_texts = {
'matter': 'message'
}
fields = ['id', 'type', 'matter']
widgets = {
'type': Select2Widget(choices=typeS)
}
class calendarForm(ModelForm):
class Meta:
model = calendar
fields = ['day','hs_init','hs_end']
Views.py
def agregarTutor(request):
if request.method == 'POST':
form = addProfesorForm(request.POST['calendar'])
calendar=calendar.objects.all()[0]
if form.is_valid():
id = form.cleaned_data['id']
try:
person_Sysatem = SysatemPerson.objects.get(pk=id)
alumn_Sysatem = SysatemAlumn.objects.get(pk=id)
except SysatemPerson.DoesNotExist:
return render(request, 'menu/new-Profesor.html',
{'new_manual': True, 'not_found': True, 'nbar': 'profesors', 'id': id})
new_Profesor = Profesor(
nombre=person_Sysatem.nombre.rstrip(),
id=person_Sysatem.numerodocu,
legajo=alumn_Sysatem.legajo,
telefono=person_Sysatem.telefono.rstrip(),
mail=person_Sysatem.mail.rstrip(),
type=form.cleaned_data['type'],
calendar=form.cleaned_data['calendar'],
matter=form.cleaned_data['matter'],
user=id,
)
Profesor.save(new_Profesor)
contrasena = 'id'+str(id)[0:5]
user = User.objects.create_user(id, person_Sysatem.mail.rstrip(), contrasena)
user.first_name = person_Sysatem.nombre.rstrip()
user.save()
form = addProfesorForm(initial={'calendar':calendar})
return render(request, 'menu/new-Profesor.html', {'form': form, 'Profesor': new_Profesor, 'success': True, 'nbar': 'profesors'})
else:
return render(request, 'menu/new-Profesor.html', {'form': form, 'error_form': True, 'nbar': 'profesors'})
else:
form = addProfesorForm()
return render(request, 'menu/new-Profesor.html', {'form': form, 'nbar': 'profesors'})
Template.html
<h2>new Profesor</h2>
<div class="row">
<div class="col">
<form method="post">{% csrf_token %}
{% include 'common/form_template.html' with form=form %}
<button type="submit" style="margin-top: 10px; float: right;" class="btn btn-primary">Add</button>
</form>
</div>
</div>