request.FILES.getlist('file') is empty - python

I am sending few files to the request with dropzone.js but the request.FILES.getlist() seems to be completely empty. Any possible reasons as to why?
--sorry that was just a typo in my question. it is FILES in my code.
def upload(request):
user = request.user
theAccount = user.us_er.account
if request.method == "POST":
form = uploadForm(request.POST)
if form.is_valid():
descriptions = request.POST.getlist('descriptions')
count = 0
for f in request.FILES.getlist('file'):
theAccount.file_set.create(docFile = f, description = descriptions[count], dateAdded = timezone.now(), creator = user.username)
count = count + 1
return HttpResponseRedirect('/home/')
else:
return HttpResponse("form is not valid")
else:
return HttpResponse('wasnt a post')
this is my template containing with the dropzone.
<form method="POST" style="border: 2px solid green;" action= "/upload/" enctype="multipart/form-data" class="dropzone">
{% csrf_token %}
<div class="dropzone-previews"></div>
<button value=" submit" class="btn btn-success" type="submit" id="submit">Press to upload!</button>
</form>

I know this is an old question, but for the sake of people landing here through google.
Dropzone.js uses ajax to upload files in a queue, so your endpoint should process the upload as a singular file not multiple.
You can access the file via request.FILES['file']

Instead of doing this:
descriptions = request.POST.getlist ('descriptions')
Try it like this:
descriptions = request.FILES.getlist ('descriptions []')
In my case it worked.

The reason for that is Dropzone's way of handling the FormData name.
TLDR
To make Dropzone Django-compliant set Dropzone's paramName option to:
paramName: (index) => 'file',
Explanation
The Django way of doing multiple files upload would be adding all the files into the FormData with the same name "file" (or whatever name you gave to Dropzone paramName option) that would end up in a single MultiValueDict called "file" in request.FILES
If we look at Dropzone's documentation, it states the following for paramName:
The name of the file param that gets transferred. NOTE: If you
have the option uploadMultiple set to true, then Dropzone will append
[] to the name.
That documentation is outdated or misleading because Dropzone does more than that according to its source code:
// #options.paramName can be a function taking one parameter rather than a string.
// A parameter name for a file is obtained simply by calling this with an index number.
_getParamName(n) {
if (typeof this.options.paramName === "function") {
return this.options.paramName(n);
} else {
return `${this.options.paramName}${
this.options.uploadMultiple ? `[${n}]` : ""
}`;
}
}
if uploadMultiple option is set to true, Dropzone will add an index between the square brackets and add them to the FormData with a name like "file[n]" where n is the index of the file.
The result in Django is a request.FILES with 'n' MultiValueDict called file[n] and you would need to call request.FILES.getlist('file[n]') for n = 0 to the number of files you sent.
Fortunately, as you can see in the comment for the function above (and that is missing from the doc), paramName can also be a function.
You can override this logic by simply ignoring the index and returning the paramName you want to handle in Django like this:
JS, Dropzone option
paramName: (n) => 'my_param_name',
Python, views.py
files = request.FILES.getlist('my_param_name')

Related

How to populate Django databases

What is the preferred way of pre-populating database (Model) objects in a Django app? I am inclined to try to script POSTing data to the relevant endpoints, but am being stymied by the CSRF protection.
This is not part of the testing framework, this is for setting up demonstration and training instances in a beta testing or production environment.
As a notional example, I'd like to populate the the "Player" database
with three entries: Alice(sender), Bob(reciever) and Charlie(eavesdropper), and I'd like to script/automate the process of creating these entries after deploying and starting the application.
I already have a form based mechanism for creating new Players. By visiting /create via a browser, there is a form that allows a person to type in the name, e.g. "Bob" and a role, e.g. "reciever", and submit the form to create the new Player instance.
Thus it makes sense to me to try to try to use the existing web API for this: e.g. make calls like
requests.post('0.0.0.0:8000/create', data={'name':'Bob', 'role':'reciever'}) in the script that pre-populates the database. However doing this results in 403 errors due to the CSRF tokens, which I don't want to disable. This problem also occurs if I just try to use a requests.Session to try to maintain the cookies between calls.
One viable solution would involve effectively managing the cookies involved to allow for posting data. However, I'm open to different ways to allow for creating model instances for initial system configuration.
Relevant code snippets:
def create(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PlayerForm(request.POST)
# check whether it's valid:
if form.is_valid():
data = form.cleaned_data
s = models.Player()
s.name = data['name']
s.role = data['role']
s.save()
msg = "TODO: make a senisble return message"
return HttpResponse(msg)
else:
msg = "TODO: make invalid sources message"
return HttpResponse(msg)
# if a GET (or any other method) we'll create a blank form
else:
form = PlayerForm()
return render(request, 'player/create.html', {'target':'/create', 'form': form})
class Player(Model):
name = models.CharField(max_length=168)
role = models.CharField(max_length=64)
class PlayerForm(forms.Form):
name = forms.CharField(label='Name:', max_length=168)
role = forms.CharField(label='Role:', max_length=64)
Note that the 'target':'/create' is the target for the form's submit action, i.e. when the user hits "Submit" the data from the form are posted to this endpoint (which then hits the if request.method == 'POST' branch to create and save the new instance.
The form is just
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="">
<style>
</style>
<script src=""></script>
<body>
<form action="{{target}}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
</body>
</html>

Thwarting form double-submission through server side tokens (Django)

I am trying to implement a server-side check to prevent users from double-submitting my forms (Django web app).
One technique I'm trying is:
1) When the form is created, save a unique ID in the session, plus pass the unique ID value into the template as well.
2) When the form is submitted, pop the unique ID from the session, and compare it to the same unique ID retrieved from the form.
3) If the values are the same, allow processing, otherwise not.
These SO answers contributed in me formulating this.
Here's a quick look at my generalized code:
def my_view(request):
if request.method == 'POST':
secret_key_from_form = request.POST.get('sk','0')
secret_key_from_session = request.session.pop('secret_key','1')
if secret_key_from_form != secret_key_from_session:
return render(request,"404.html",{})
else:
# process the form normally
form = MyForm(request.POST,request.FILES)
if form.is_valid():
# do something
else:
# do something else
else:
f = MyForm()
secret_key = uuid.uuid4()
request.session["secret_key"] = secret_key
request.session.modified = True
return render(request,"my_form.html",{'form':f,'sk':secret_key})
And here's a sample template:
<form action="{% url 'my_view' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="sk" value="{{ sk }}">
{{ form.my_data }}
<button type="submit">OK</button>
</form>
This set up has failed to stop double-submission.
I.e., one can go on a click frenzy and still end up submitting tons of copies. Moreover, if I print secret_key_from_form and secret_key_from_session, I see them being printed multiple times, even though secret_key_from_session should have popped after the first attempt.
What doesn't this work? And how do I fix it?
UPDATE: when I use redis cache to save the value of the special key, this arrangement works perfectly. Therefore, it seems the culprit is me being unable to update request.session values (even with trying request.session.modified=True). I'm open to suggestions vis-a-vis what could be going wrong.
Note that this question specifically deals with a server-side solution to double-submission - my JS measures are separate and exclusive to this question.
You might just need request.session.modified = True. If you want to make sure that the session is deleting you can use del too.
secret_key_from_session = request.session.get('secret_key','1')
del request.session['secret_key']
request.session.modified = True
I couldn't figure out what caused the problem, but via substituting Redis cache for every request.session call, I was able to get my desired results. I'm open to suggestions.

Pass Django variables between view functions

I'm asking a question about variables handling in my Django application view.
I have 2 functions :
The first one lets me to display query result in an array with GET filter parameter (in my case, user writes year and Django returns all objects according to this year. We will call query_naissance this variable).
The second one lets me to create a PDF. I have lots of variables but I want to take one more time query_naissance in my PDF.
This is my first function :
#login_required
def Table_annuelle_BirthCertificate(request) :
query_naissance = request.GET.get('q1')
...
return render(request, 'annuel.html', context)
And my second function looks like :
#login_required
def Table_Naissance_PDF(request) :
data = {"BirthCertificate" : BirthCertificate}
template = get_template('Table_raw.html')
html = template.render(Context(data))
filename = str('Table annuelle Naissance.pdf')
path = '/Users/valentinjungbluth/Desktop/Django/Individus/' + filename
file = open(path, "w+b")
pisaStatus = pisa.CreatePDF(html.encode('utf-8'), dest=file, encoding='utf-8')
file.close()
context = {
"BirthCertificate":BirthCertificate,
"query_naissance":query_naissance,
}
return render(request, 'Table.html', context) # Template page générée après PDF
So How I can add query_naissance given by user in my first function to my second one without write one more time a field ?
Then, I have to call this variable like {{ query_naissance }} in my HTML template.
Thank you
In order to persist information across requests, you would use sessions. Django has very good session support:
# view1: store value
request.session['query_naissance'] = query_naissance
# view2: retrieve vlaue
query_naissance = request.session['query_naissance']
# or more robust
query_naissance = request.session.get('query_naissance', None)
You need 'django.contrib.sessions.middleware.SessionMiddleware' in your MIDDLEWARE_CLASSES.

Dajax/Dajaxice saving object in ajax.py with parameters

I've run through setup and have all examples on dajaxproject.com running fine, but I'm now having problems using what I've learnt in a more complex use case. I'd like to pass several parameters to the ajax function, along with text from a form, and create an object using those pieces of data.
If anybody can help me out, it would be hugely appreciated.
I'm using jquery and jquery.ba-serializeobject.min.js.
Ajax.py:
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
comment_form = CreateCommentForm(deserialize_form(form))
if comment_form.is_valid():
text = comment_form.cleaned_data['text']
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
comment = other_profile.comments.objects.create(owner=user, text=text)
comment.save()
return dajax.json()
JS:
<script type="text/javascript">
function add_comment(){
var user = '{{ person.user.username }}';
var other = '{{ other.user.username }}';
var data = $('#comment_form').serialize(true);
Dajaxice.profiles.save_comment(Dajax.process, {'form': data, 'user_username': user, 'other_username': other });
return false;
}
</script>
HTML:
<div><h4>Post Comment:</h4>
<div id="comment_form_errors"></div>
<form action="" id="comment_form" accept-charset="utf-8" method="post">
{% csrf_token %}
{{ commentform.as_p }}
<p><input class="btn profile-comment-submit" id="submit_profile_comment" onclick="add_comment()" type="submit" alt="register" /></p>
<form>
</div>
In Chrome's debug console the only error I get is Dajaxice: something went wrong.
If I've left anything out that might be important, let me know.
Many thanks,
The only thing that stands out to me (and I'm no expert, so who knows...) is in your ajax.py. I think it should be:
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
comment_form = CreateCommentForm(deserialize_form(form))
if comment_form.is_valid():
text = comment_form.cleaned_data['text']
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
comment = Comment(owner=user, text=text)
comment.save()
other_profile.comments.add(comment)
# I don't think you need to other_profile.save(), but you can if you want
return dajax.json()
The way you send the form is crusial for Dajax to work. I have succeeded using http://benalman.com/projects/jquery-misc-plugins/#serializeobject and the following javascript:
jQuery('form').submit(function(e) {
e.preventDefault()
var data = jQuery(this).serializeObject();
Dajaxice.app.app_name.function_name(Dajax.process,{'form':data});
return false;
});
It is a little hard to get a complete overview of the problem when I don't see your form. But I would recommend you to create a form CommentForm and populate user and other_user in hidden fields on form initialization. That will make the code less complex
Your save function will then be quite simple:
#dajaxice_register
def function_name(request, form):
dajax = Dajax()
form = CommentForm(form)
if form.is_valid():
form.save()
return dajax.json()
There are a few things that I can see here, but without being able to see the CreateCommentForm() and the model that it is creating a form for some of this may be assumption based. Also assuming there are not any problems with the serialization of the form.
#dajaxice_register
def save_comment(req, form, user_username, other_username):
dajax = Dajax()
user = User.objects.get(username=user_username)
other_user = User.objects.get(username=other_username)
other_profile = Profile.objects.get(user=other_user)
# Required fields of a form must be checked before calling is_valid() or is_valid fails.
comment = Comments(owner=user)
comment_form = CreateCommentForm(deserialize_form(form), instance=comment)
if comment_form.is_valid():
comment_form.save()
dajax.alert('Form is valid')
else:
dajax.alert('Form is invalid')
for error in comment_form.errors:
dajax.add_css_class('#id_%s' % error, 'error')
# Dajax needs something added to it before the dajax.json() can be returned.
return dajax.json()
The form piece can be referred to here: Django using a subset of fields on the form
and the dajax return pieces can be seen in more detail in this dajax example: Dajax form validation example
I've found no way to make it work. I think it is an issue with Dajaxice, what you can do is to avoid the request.POST QueryDict and instead use the request.raw_post_data. You'll need to do the reverse of urlparse:
data = urlparse.parse_qs(request.raw_post_data)
Then you'll need to deserialize it.
data = json.loads(data.get('argv'))
That will return a list of arguments, use the first element in the list.

POSTing forms in Django's admin interface

I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/

Categories