Retaining uploaded file (FileField) when form is not valid - django

Given a Model with a required field called number and a ClearableFileInput FileField called upload_file:
class ExampleModel(models.Model):
number = models.IntegerField()
upload_file = models.FileField(blank=True, null=True)
In my view, on POST, if the form is_valid then I can populate the clearable part of the FileField when returning to the same page.
def example_view(request):
context = RequestContext(request)
if request.method == 'POST':
form = ExampleForm(request.POST, request.FILES)
if form.is_valid():
form_instance = form.save()
form = ExampleForm(instance=form_instance)
# or alternatively, for just the upload file field
form = ExampleForm(initial={'upload_file': form_instance.upload_file})
else:
form_instance = form.save(commit=False)
form = ExampleForm(initial={'upload_file': form_instance.upload_file})
# unfortunately, this also does not work:
form = ExampleForm(initial={'upload_file': form.fields['upload_file']})
else:
form = ExampleForm()
return render_to_response('enterrecords/example.html', {'form': form}, context)
This is how it looks:
However, if the form is not valid (see first else case), I can not form.save(commit=False) and therefore cannot populate the clearable part of the FileField.
The form.save(commit=False) gives the following error:
ValueError at /en/myapp/example/
The ExampleModel could not be created because the data didn't validate.
Is there a workaround for this problem?
For completeness...
ModelForm
class ExampleForm(forms.ModelForm):
class Meta:
model = ExampleModel
Template
<form enctype="multipart/form-data" method="POST" action="{% url 'eg' %}">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" name="next" value="Next" />Next</button>
</form>

Use the form.clean_xxx with xxx being the field to clean in your form. It is called before the call to is_valid() of your view.
Here's a sample of what I do when I get an Excel file in my form (my UploadedFileHandler class is useless here, it's just to show you the principle).
The idea is that, even if the form is not valid, the Excel file is always saved and thus I keep a trace of what happened:
def clean_excel_file(self):
uploaded_file = self.files.get('excel_file')
if uploaded_file:
try:
nom = UploadedFileHandler.generate_filename(
splitext(basename(uploaded_file.name))[1])
dst = UploadedFileHandler.get_url(nom, 'imports/excel/')
# sauver le fichier
dst_full = UploadedFileHandler.full_filename(dst)
UploadedFileHandler.make_dir(dst_full)
UploadedFileHandler.save(uploaded_file, dst_full)
retour = ExcelFile.objects.create(
# description = nom de fichier sans l'extension :
description=path.splitext(basename(str(uploaded_file)))[0],
fichier_origine=uploaded_file,
excel_file=dst)
retour.save()
return retour
except IOError:
self.errors['excel_file'] = ErrorList([_("Unknown file type")])
return None

Related

Validation for current user

How to realize checking 'name' for current user in forms.py in ValidationError('Same name already added, change name').
views.py
#login_required
def main_page(request):
form = URL_listForm(request.POST)
if request.method == "POST":
if form.is_valid():
name = form.cleaned_data['name']
if URL_list.objects.filter(user=request.user, name=name).exists():
return HttpResponse('Same name already added, change name')
new_post = form.save(commit=False)
new_post.user = request.user
new_post.save()
return HttpResponse("Data added")
return render(request, 'link/main.html', {'form': form})
If you want validate in database
#-------ADDED CODE
data_tmp = """SELECT count(*) from jobtest WHERE link = %s""", (line)
data_tmp = cur.fetchall()
#-------END ADDED CODE
if (data_tmp == 0 ) :
Not exist
add form with name
<input type="text" id="projectName" size="40" placeholder="Spot your project files">
<input type="button" id="spotButton" value="Spot">
when press post button and action to api you can get value in input field using request.form['Name']
if you want send data from server code to html
return render_template('index.html', data=userinfo)
and render as
{% userinfo %}

File uploaded in my form is not showing up in my edit/update form

When I fill out my form with its file, it apparently submits, but when checking it out when editing/uploading, all data in the form shows up except my file. So, I cannot access it at all. I guess it probably has something to do with my views.py (the function is for both, save a form for the first time and visualizing them), so here it goes
#views.py
def contract_form(request, id=0):
if request.method == "GET":
if id == 0:
form = ContractsForm(request.FILES)
else:
contratos = Contratos.objects.get(pk=id)
form = ContractsForm( instance=contratos)
return render(request,"contracts_form.html", {'form':form})
else:
if id == 0:
form = ContractsForm(request.POST, request.FILES)
else:
contratos = Contratos.objects.get(pk=id)
form = ContractsForm(request.POST, request.FILES, instance= contratos, )
if form.is_valid():
form.save()
messages.success(request, "Form submission successful")
else:
messages.error(request, "Error, contract not submitted")
return redirect('/contracts')
Just in case, every other instance that has something to do with the upload file:
#models.py
attached_file=models.FileField(upload_to="media", blank=True)
My form is stated with <form enctype="multipart/form-data" action="" method="post" autocomplete="off" class="row g-3">
#contracts_form.html
<label for="{{ form.subject.id_for_label }}">Attached File:</label>
{{form.attached_file}}
{% if Contracts.media %}
{% endif %}
#settings.py
MEDIA_ROOT=os.path.join(BASE_DIR, 'uploads/')
MEDIA_URL="/contracts-media/"
path('contracts/edit/<int:id>', views.contract_form, name='edit'), #edit/update form
path('contracts/add', views.contract_form, name = 'new-contract'), #add a contract
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
````
There's no text in your link tag
Try
Wonderful Linkiness

Unique=True in Form not throwing any error

I'm entering a duplicate value (already saved in another instance of the same model) in my form to test the unique=True attribute. form.is_valid() returns 'False', as expected, but I don't receive any prompt in the template. Shouldn't I get prompted something like "obj with this value already exists"? The page simply reloads... What am I missing?
forms.py
def update_route(request, pk):
instance = Route.objects.get(id=pk)
if request.method == "POST":
form = RouteForm(request.POST)
if form.is_valid():
data = form.cleaned_data
instance.name = data['name']
instance.priority = data['priority']
instance.url = data['url']
return redirect('campaigns:routes_list')
form = RouteForm(instance=instance)
context= {
'form': form,
}
return render(request, "campaigns/route_form.html", context)
models.py
class Route(models.Model):
name = models.CharField(max_length=48)
priority = models.SmallIntegerField(choices=PRIORITY_LEVEL, default=0, unique=True)
url = models.URLField()
Template
<form method="post" action="">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Submit">
</form>
Your update_route() view handles the condition in which the submitted form is valid (form.is_valid()), but not the condition in which the form is invalid.
The errors you are looking for are stored in the form object that you created with RouteForm(request.POST). The errors are generated when the is_valid() method is called.
This form object needs to be added to the context dict and rerendered to the user for the errors to surface. But your code currently overwrites that object with form = RouteForm(instance=instance), so the POST data and the related errors disappear.
One solution could be to handle it in the conditional statement:
if form.is_valid():
...
else:
context = {'form': form}
return render(request, "campaigns/route_form.html", context)
Another solution could be to create a conditional statement for GET requests, for example:
elif request.method == 'GET':
form = RouteForm(instance=instance)

Django form not calling custom validator

I'm using Django 2.0. This is my forms.py:
class PostcodeForm(forms.Form):
postcode = forms.CharField(required=True, widget=forms.TextInput(
attrs={
'placeholder': "enter a postcode",
}
))
def clean_postcode(self):
postcode = self.clean_data.get('postcode', '')
print('clean_postcode', postcode)
if postcode != 'something':
raise forms.ValidationError(_("Please enter a valid postcode"), code='invalid')
return data
And my views.py:
def index(request):
form = PostcodeForm()
context = {
'form': form
}
return render(request, 'index.html', context)
And my index.html:
<form class="form-inline" id="lookup_postcode" action="{% url 'lookup_postcode' %}" method="get">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.postcode.errors }}
{{ form.postcode }}
<button type="submit">Submit</button>
</form>
But when I type in any value other than 'something', the form still submits. I also don't see any print statements in the console, so it looks as though the validator just isn't being run.
What am I doing wrong?
At the moment you are always doing form = PostcodeForm(), for GET and POST requests. That means that the form is not bound to any data, so it will never be valid or have any errors.
In Django, a typical view to process a form looks something like this:
from django.shortcuts import redirect
def index(request):
if request.method == 'POST':
form = PostcodeForm(request.POST)
if form.is_valid():
# form is valid. Process form and redirect
...
return redirect('/success-url/')
else:
form = PostcodeForm()
context = {
'form': form
}
return render(request, 'index.html', context)
For this to work, you'll need to change your form method to 'post'.
<form class="form-inline" id="lookup_postcode" action="{% url 'lookup_postcode' %}" method="post">
If you keep the form method as 'get' then you'll need to bind the form to request.GET instead. You might want to add a check, otherwise you'll get errors for required fields when you first access the index view.
if 'postcode' in request.GET:
# bound form
form = PostcodeForm(request.GET)
else:
# unbound, empty form
form = PostcodeForm()
Use your form as below:
class PostcodeForm(forms.Form):
postcode = forms.CharField(required=True, widget=forms.TextInput(
attrs={
'placeholder': "enter a postcode",
}
))
def clean(self):
postcode = self.cleaned_data.get('postcode', '')
print('clean_postcode', postcode)
if postcode != 'something':
raise forms.ValidationError(_("Please enter a valid postcode"), code='invalid')
return super(PostcodeForm, self).clean()
Everytime you deal with the validity of the posted data, make sure to include form.is_valid() condition in your views.py.

Django custom form validation in ListView

I am using a ListView to set a form and to show results. However i am not sure how can I make form validation and having the same form with errors in case form.is_valid() is not True.
this is my code
forms.py
class InsolventiForm(forms.Form):
anno_validator = RegexValidator(r'[0-9]{4}', 'L\'anno deve essere un numero di 4 caratteri')
anno = forms.CharField(label='Anno', required=True, max_length=4,validators=[anno_validator])
def clean_anno(self):
anno = self.cleaned_data['anno']
return anno
views.py
from .forms import InsolventiForm
class InsolventiView(LoginRequiredMixin, ListView):
template_name = 'insolventi.html'
model = Archivio
form_class = InsolventiForm
def get(self, request):
import datetime
if self.request.GET.get('anno'):
form = self.form_class(self.request.GET)
if form.is_valid():
date = '31/12/'+self.request.GET.get('anno')
dateTime = datetime.datetime.strptime(date, "%d/%m/%Y")
dateC = '01/01/'+self.request.GET.get('anno')
dateTimeC = datetime.datetime.strptime(dateC, "%d/%m/%Y")
context = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('anno')) \
& Q(data_iscrizione__lte=dateTime) \
& (Q(cancellato__exact=False) | (Q(cancellato__exact=True) & (Q(data_canc__gte=dateTimeC)))))
self.request.session['insolventi_queryset'] = serialize('json', context)
return render(request, self.template_name, {'form':form})
else: return redirect(reverse('insolventi'))
return render(request, self.template_name, {'form':self.form_class()})
this is my template and I am displaying the form manually.
insolventi.html
<form method="get" action="">
{% for field in form %}
{{ field.errors }}
{{ field.as_widget() }}
{% endfor %}
<input type="submit" value="Ricerca" />
</form>
Even if there are errors and form.is_valid() is returning False (giving me a redirect to the same view) on the template I never get {{ form.errors }}.
I don't know what is missing!
I am thinking: Because i use the input of the form to get the query in JSON with django rest and post it on the same template with DataTables, maybe I do not need to use a ListView ??
You should not be redirecting if there are errors since redirecting will lose all the form data.
Try removing the line:
else: return redirect(reverse('insolventi'))
and letting it fall through to the render() line.
Hi can you try this post
custom form validation
also refer django document
django custom validation as per document