Upload file with ajax call in django - python

I created form upload file in django. it is working. But when i try with ajax in django file upload is not working.
My code:
models.py
class Upload(models.Model):
documents = models.FileField(upload_to=content_file_name)
user = models.ForeignKey(User)
class Meta:
app_label = 'documentapp'
forms.py:
from django import forms
class UploadForm(forms.Form):
documents = forms.FileField()
html:
<input type="file" name="documents" id="documents" class="form-control" required="required""/>
<input type="button" id="upload_file_button" required="required" value="Upload" onclick="UploadUserDocuments()"/>
ajax in same html file:
function UploadUserDocuments() {
$.ajax({
url: "/signup/upload/",
type: "POST",
data: {
documents:$('education_documents').val(),
},
success: function(response){
}
});
}
view.py
def upload(request):
context = RequestContext(request)
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return HttpResponse('true')
else:
return HttpResponse('GET')
handle_uplaoded_fiel.py
def handle_uploaded_file(f):
with open('some/file/name.txt', 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)

There are several problems with the HTML/JS.
The first line of your HTML has a double terminating quotation for the value of the required attribute.
Your UploadUserDocuments() function does the following:
$('education_documents').val()
which is attempting to match an element such as <education_documents> rather than the usual way of selecting the value of an element by its id (i.e. using the # selector).
However, it's more involved to perform a file upload using JQuery. This answer has some ideas: How can I upload files asynchronously?

It seems that you don't send csrf_token. You can skip it for you view(which is not really recommended) by using csrf_exempt decorator.
Otherwise you should add {%csrf_token%} tag to your template. It will create hidden field with CSRF token. The way how to handle it in AJAX you can find in official documentation.
Also I'm curious why don't you render form from variable, but add HTML manually instead - but it isn't directly related to the topic.
So I'd to do it like:
html:
<form id="upload_doc" action="/signup/upload/" method="post" enctype="multipart/form-data"> {% csrf_token %}
{{form}}
<input type="submit" value="send">
</form>
Also please see notices from #Ben Rowland answer

Related

Django, pandas, excel: uploading files, parsing them with pandas in Django

I have a big command-line script for parsing data in Excel (with pandas) and I want to wrap it with Django. I've tried both uploading files thru request.FILES and pandas, but get stuck on uploading file and, for example, saving it (not necessarily but just to check the upload for now).
Haven't had any problems with other apps on Django which didn't require uploading and parsing anything external and thought that would be much easier..:)
I've also tried Redirecting, doesn't really work, the only redirect which is actually happening is action in the form tag..
Here are the code snippets:
views.py:
def uploads(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
excel_file = request.FILES['document']
excel_file.save()
return render(request, 'index.html')
else:
form = DocumentForm()
return render(request, 'index.html', {'form': form})
models.py
class Document(models.Model):
document = models.FileField(upload_to="files/")
forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('document', )
index.html:
<form
action="{% url 'reports'%}"
method="post"
enctype="multipart/form-data"
>
{% csrf_token %}
<span>
Upload .xlsx file <input type="file" name="document" />
</span>
<button type="submit">
SUBMIT
</button>
</form>
I think you must save the form's content actually:
form.save()
The reason was that I had another function in views.py leading to the same url route.

is there a way simillar to this way to save files (.docx, pdf...) in django?

I've been lately wondering how to save file for each user using django , i kinda thought about this way:
the html page:
<form acrion ="{% url 'register'%}">
name: <input type="text" name ="name">
resume: <input type="file" name ="myfile">
<input type="button" value="signup">
</form>
the modes.py file:
class someOne(models.Model):
name = models.CharField(max_length = 200)
resume = models.FielField(upload_to="wherever i want")
the views.py file
def register(request):
if request.method == 'POST':
name = request.POST['name']
resume = request.POST['resume']
new_user = someOne(name,resume)
new_user.save()
is there anyway should be easy like this or i need to search another way hope you guys undrestand me thanks.
Yes, absolutely you can do that. but you have to make little changes in your code.
add enctype to multipart form data in your html form like <form acrion ="{% url 'register'%}" enctype="multipart/form-data">
and use request.FILES for getting file in your view.
You can use this decent tutorial for your reference :
https://www.geeksforgeeks.org/python-uploading-images-in-django/

Python - Django 2.0 - URL patterns, passing arguments

I am writing a very basic web page in Python which has a text box where a user can type in a username, then hit the Ok button which submits a form using a GET request. The GET passes the username as an argument and searches the auth_user table in the database.
My problem is I am not able to pass the username argument, please help if you can Django 2.0 url patterns
urls.py
app_name = 'just_gains'
urlpatterns = [
path('lifecoaching', views.LifeCoach, name='life_coaching'),
path('lifecoaching/resultslifecoaching/<str:user_name>', views.LifeCoachSearchResults, name='results_life_coaching'),
]
forms.py
class LifeCoachSearch(forms.Form):
user_name = forms.CharField(label='Username', max_length=100, required = False)
views.py
def LifeCoach(request):
if request == 'GET':
form = LifeCoachSearch(request.GET)
if form.is_valid:
user_name = form.cleaned_data['user_name']
LifeCoachSearchResults(request,user_name)
else:
form = LifeCoachSearch()
return render(request, 'just_gains/life_coaching.html', {'form': form})
def LifeCoachSearchResults(request, user_name):
testUser = User.objects.filter(username__startswith=user_name)
context = {'TestUser': testUser}
return render(request, 'just_gains/results_life_coaching.html', context)
HTML (lifecoaching)
<form action="{% url 'just_gains:results_life_coaching' %}" method="GET" >
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
HTML (resultslifecoaching)
<ul>
<li><a>print usernames that match the argument</a></li>
</ul>
Forgive me for the short response as I am on mobile. Try passing your username as a string in the path using <str:user_name>
Usually I think the form should submit via POST rather than GET, and the value of the submitted username would then be available in the dictionary request.POST['username']. GET should be used to get forms from the server; POST posts information back to the server. POST ensures that the browser bundles everything in the form and sends it complete, but GET tries to encode it in the URL and makes no guarantees.
Using forms, its helpful to have the View divide so that GET requests pull up blank or prepopulated forms (the empty search box) and POST requests are processed and redirected to the parameterized results screen you have.
You would then create a httpRedirect to re-assign the request to your URL with a parameter. I think this link, example 2 is the right way to go.
https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#redirect
So your function would look like:
def LifeCoach(request):
if request.method = 'GET':
return render(request, 'just_gains/life_coaching.html', context)
elif request.method = 'POST':
# I have skipped form validation here for brevity
return redirect('results_life_coaching',request.POST['username'])
It's possible that having a field called username may clash with or confuse you later when using request.USER['username']. Don't forget to change your form html! All the best!
[Edit 1] My code was wrong; GET should call the lifecoaching form, POST should redirect to the results_life_coaching page.
[Edit 2] My suggestions for your templates:
HTML (lifecoaching.html)
<form action="{% url 'just_gains:life_coaching' %}" method="POST" >
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
HTML (resultslifecoaching.html)
<ul>
{% for item in username_list %}
<li>{{item.user_name}} - {{item.achievement}} </li>
{% endfor %}
</ul>

Clean Django form fields with same name

I have a django template in which I'm dynamically rendering multiple fields (using ajax)
Below is a Django form (which has been rendered in a template) whose fields have same names. I want to use the cleaned_data method to clean form data in views.py before storing them in the database.
index.html
<div class="form-container">
<!-- ASSUMING I HAVE ALREADY ADDED FIELDS DYNAMICALLY -->
<form id = "orderForm" action="newPickupOrder/" method="post" name="processForm">
<input type='text' name='this_field'>
<input type='text' name='this_field'>
<button type="submit">Submit</button>
</form>
</div>
<form id="addItemForm">
{% csrf_token %}
<!-- BUTTON TO ADD MORE FIELDS DYNAMICALLY -->
<button id = "addItemButton">Add item</button>
</form>
<script>
var addItemButton = document.querySelector('#addItemButton');
addItemButton.onclick = function(){
$.ajax({
type: 'POST',
url: 'addItem/',
data: addItemForm.serialize(),
success: function (response) {
$("#orderForm").append(response);
console.log('Success');
},
error: function (response) {
console.log('Error = '+response);
}
});
};
</script>
forms.py
class ItemForm(forms.Form):
this_field = forms.CharField()
urls.py
urlpatterns = [
url(r'^newPickupOrder/$', views.pickup_order_view, name='new_pickup_order'),
]
views.py
def add_item(request):
if request.method == 'POST':
itemForm = ItemForm()
return HttpResponse(itemForm.as_p())
def pickup_order_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
same_name_fields = request.POST.getlist('this_field')
# WANT TO CLEAN DATA IN same_name_fields
if form.is_valid():
print(form.cleaned_data)
# ONLY PRINTS THE LAST FIELD's DATA
return HttpResponseRedirect('/viewPickupRequests')
The problem I'm facing is that if I use form.cleaned_data['this_field'], only the last field's data is fetched i.e. in this example, the field with value anotherTestValue is fetched and cleaned. If I fetch the data using request.POST.getlist('this_field'), all the fields' data is fetched and stored as a list, but, I don't know how to clean it using cleaned_data method. Is there a way to apply the cleaned_data method to the list of field data?
I'm sorry, I can't test if this works so this is not really an answer - but the comment system is not suitable for larger code chunks so I'm posting here.
Django forms lack a field type that renders to multiple text inputs with the same name. The proper thing to do would be to write a new form field class and a new widget. Since you are not rendering the form in the template (you are using it only for validation) I will omit the widget part.
class AcceptAnythingMultipleChoiceField(forms.MultipleChoiceField):
def validate(self, value):
if self.required and not value:
raise ValidationError(
self.error_messages['required'],
code='required'
)
Then use this field class instead of forms.CharField() (you may need to pass an empty choices parameter).
[update]
So essentially what you're saying is that I need to create new form field class and then render it to the template each time the user wants to add a new field? What if user has to add 15 fields, I'll need to create 15 classes then! I think this method won't be suitable in scenarios where number of fields required to be generated is large. I feel there should be some elegant way to do this which i'm not aware of – The OP
No, it is not what I'm saying. You probably want to subclass something like MultipleHiddenInput and set AcceptAnythingMultipleChoiceField.widget to it. You will have to create a new template based on the template for MultipleHiddenInput and replace input type="hidden" for type="text" (the original template is django/forms/widgets/multiple_hidden.html).
class AcceptAnythingWidget(MultipleHiddenInput):
template_name = 'django/forms/widgets/multiple_visible.html'
class AcceptAnythingMultipleChoiceField(forms.MultipleChoiceField):
widget = AcceptAnythingWidget
def validate(self, value):
if self.required and not value:
raise ValidationError(
self.error_messages['required'],
code='required'
)
This should render as many <input name='this_field'> as needed for instantiated forms at the frontend if you use:
{{ form.this_field }}
in the template, but will not add/remove them dynamically.
In order to do that you must plug in the JavaScript required to add/remove inputs dynamically in the widget but I will left this as an exercise for you. Look at Form Assets (the Media class) in the docs in order to figure out how to do that.
I think that what you are looking for is formsets. https://docs.djangoproject.com/en/2.0/topics/forms/formsets/
from django.forms import formset_factory
ItemFormSet = formset_factory(ItemForm, extra=2)
You can the essentialy use ItemFormSet in the way you would use a normal form except that this objects is iterable.
You will also have to change your jquery if you want to dynamically add items. There are many examples online on how to do this. In short what you do is
clone one of the forms in the formset
clear all the values from the copied form
update the input's (prefixes of) id's
Using Formsets doesn't solve the problem of fetching and validating
fields with same name. The issue still remains
It does however generate the end result you wanted (see below). My question would be why you need to have inputs with the same name? If there is some jquery stuff that uses these names I dont see any reason why you wouldn't be able to use name like... or assign a class to the inputs instead.
def pickup_order_view(request):
if request.method == 'GET':
ItemFormSet = formset_factory(ItemForm, extra=5)
item_formset = ItemFormSet()
template = "some_template.html"
template_context = {'item_formset': item_formset}
return render(request, template, template_context)
if request.method == 'POST':
ItemFormSet = formset_factory(ItemForm)
item_formset = ItemFormSet(request.POST)
same_name_fields=[]
if item_formset.is_valid():
for item_form in item_formset:
same_name_fields.append(item_form.cleaned_data['this_field'])
print(same_name_fields)
Template
<form id = "orderForm" action="newPickupOrder/" method="post" name="processForm">
{% csrf_token %}
{{ item_formset.management_form }}
{{ for item_form in item_formset }}
{{ item_form.as_p }}
{{ endfor }}
<input type='submit' value='submit'>
</form>
Go to newPickupOrder/ , fill in the 5 fields, hit submit, and watch it print your list.

Django - prevent CreateView being called twice

What I need to do is access the same CreateView from two different pages. One of those two pages will send along some data that will be inserted into the CreateView form via get_initial(). The button I'm using to access the view is sending some POST data and so the CreateView is immediately thinking everything was successful and going all the way to get_success_url() and then rerouting. Below is what I have. I'm not sure the most elegant way to achieve what I wanted. I was trying to avoid any GET data (making the URLs messy) if possible? This there an elegant way to send POST data to the CreateView upon initialization?
template.html
<form method="POST" action="{% url 'purification:add' %}">
{% csrf_token %}
<input type="hidden" name="sample_id" value="{{ sample_data.id }}">
<button class="btn btn-success" id="add-purification-button" role="button" type="submit">+Add Purification</button>
</form>
view.py
class PurificationCreateView(LoginRequiredMixin, CreateView):
model = Purification
template_name = "add_purification.html"
fields = "__all__"
def get_initial(self):
if self.request.POST.get('sample_id'):
sample = Samples.objects.get(pk=self.request.POST['sample_id'])
return {'sample': sample}
return None
def get_success_url(self):
if "save-and-add-another" in self.request.POST:
return reverse('purification:add')
else:
return reverse('samples:oneSample', kwargs={'sample_id': self.request.POST['sample']})

Categories