I am trying to subclass an ajax uploader to accept a hash_id of a django model (so that it may create the model upon the successful upload of an image) and am having trouble passing the additional kwarg (widget2_hash_id). I would appreciate guidance on how to properly add the kwarg.
views.py:
class S3UploadBackend_Widget2EditableImage(S3UploadBackend):
def upload(self, *args, **kwargs):
self.widget2_hash_id = kwargs.pop('widget2_hash_id')
k = Key(self._bucket)
chunk = uploaded.read()
k.set_contents_from_string(chunk)
# create uploaded file
fh = tempfile.TemporaryFile()
k.get_contents_to_file(fh)
fh.seek(0)
saveable_file = SimpleUploadedFile(k.name, fh.read())
# delete aws key and close tempfile
_media_bucket.delete_key(k)
fh.close()
self.widget2 = Widget2.objects.get(hash_id = self.widget2_hash_id)
self.widget2_editable_image = Widget2EditableImage(image = saveable_file, widget2 = self.widget2)
self.widget2_editable_image.save()
if k.key:
self.key = k.key
return True
else:
# Key creation failed.
return False
def upload_complete(self, request, filename):
# Manually add S3 key to ajaxuploader JSONresponse
res = {"aws_file_key": self.key, "url": self.widget2_editable_image.image.url}
views.py:
widget2_editable_image_ajax_uploader = AjaxFileUploader(backend=S3UploadBackend_Widget2EditableImage)
urls.py:
(r'^widget2/widget2_image_upload/(?P<widget2_hash_id>[a-fA-F0-9]+)/$', 'widget2.views.widget2_editable_image_ajax_uploader'),
Traceback:
Traceback (most recent call last):
File "/home/zain/XXX/lib/Django-1.3.1/django/core/handlers/base.py", line 111, in get_response
response = callback(request, *callback_args, **callback_kwargs)
TypeError: __call__() got an unexpected keyword argument 'widget2_hash_id'
[20/Aug/2012 20:50:44] "POST /widget2/widget2_image_upload/d9dc4fab3d5e0eb45995/?qqfile=s3Zas.jpg HTTP/1.1" 500 870358
EDIT: I tried this inside the class and get the same error:
def __init__(self, *args, **kwargs):
try:
self.widget2 = Widget2.objects.get(hash_id = kwargs.pop('widget2_hash_id'))
except KeyError:
self.widget2_hash_id = None
super(S3UploadBackend_Widget2EditableImage, self).__init__(*args, **kwargs)
EDIT2: here is the AjaxFileUploader class:
class AjaxFileUploader(object):
def __init__(self, backend=None, **kwargs):
if backend is None:
backend = LocalUploadBackend
self.get_backend = lambda: backend(**kwargs)
def __call__(self, request, **kwargs):
return self._ajax_upload(request)
def _ajax_upload(self, request):
if request.method == "POST":
if request.is_ajax():
# the file is stored raw in the request
upload = request
is_raw = True
# AJAX Upload will pass the filename in the querystring if it
# is the "advanced" ajax upload
try:
filename = request.GET['qqfile']
except KeyError:
return HttpResponseBadRequest("AJAX request not valid")
# not an ajax upload, so it was the "basic" iframe version with
# submission via form
else:
is_raw = False
if len(request.FILES) == 1:
# FILES is a dictionary in Django but Ajax Upload gives
# the uploaded file an ID based on a random number, so it
# cannot be guessed here in the code. Rather than editing
# Ajax Upload to pass the ID in the querystring, observe
# that each upload is a separate request, so FILES should
# only have one entry. Thus, we can just grab the first
# (and only) value in the dict.
upload = request.FILES.values()[0]
else:
raise Http404("Bad Upload")
filename = upload.name
backend = self.get_backend()
# custom filename handler
# Override filename to avoid collisons
filename = unicode(hashlib.sha1(str(datetime.datetime.now())).hexdigest()[0:6]) + filename
filename = (backend.update_filename(request, filename)
or filename)
# save the file
backend.setup(filename)
success = backend.upload(upload, filename, is_raw)
# callback
extra_context = backend.upload_complete(request, filename)
# let Ajax Upload know whether we saved it or not
ret_json = {'success': success, 'filename': filename}
if extra_context is not None:
ret_json.update(extra_context)
return HttpResponse(json.dumps(ret_json, cls=DjangoJSONEncoder))
The explanation
In your urls.py, you have this:
(r'^widget2/widget2_image_upload/(?P<widget2_hash_id>[a-fA-F0-9]+)/$', 'widget2.views.widget2_editable_image_ajax_uploader'),
When this url gets matched, django will dispatch the request, as well as the args to the given view, so, if we break it down, the following arguments will be passed to widget2.views.widget2_editable_image_ajax_uploader (the view):
request
*args: []
*kwargs: {'widget2_hash_id':'somehashid',}
Now, what does dispatch mean in django vocabulary? Well, basically, it means call function.
It so happens that widget2.views.widget2_editable_image_ajax_uploader has a call method, so django will happily call this method with the arguments mentionned above.
Unfortunately, as your view does not accept the widget2_hash_id kwarg, python is going to raise an error. Which it does.
I think that the point were you got lost is that you missed the fact that it's the view that gets passed the extra arg, not the backend. It's then the view's job to pass it down to the backend.
The solutions
The easy one
You just need to modify your __call__ method so that it accepts the additional arg, and then passes it on to the _ajax_upload method.
Then, you need to pass the argument to your backend. To this end, you should change the following line:
backend.upload(upload, filename, is_raw)
to
backend.upload(upload, filename, is_raw, widget2_hash_id)
The right one
Now, I must say that I'm a bit confused as to what you are doing. You seem to be using objects as views, but then, why wouldn't you use django's awesome class-based views?
The documentation used to be pretty lacking (I honestly can't tell if that's still the case), so here's a small guide to get you started.
Take some time to study and use them. They are very easy to use and extend, and they will save you a huge amount of time in the long run.
On a closing note
Please do use django forms, because they rock.
Related
In my view function I want to call another view and pass data to it :
return redirect('some-view-name', backend, form.cleaned_data)
, where backend is of registration.backends object, and form.cleaned_data is a dict of form data (but both must be either sent as *args or **kwargs to prevent raising Don't mix *args and **kwargs in call to reverse()! error). From what I've found in the docs :
def my_view(request):
...
return redirect('some-view-name', foo='bar')
It looks like I need to provide 'some-view-name' argument, but is it just the name of the view function, or the name of the url ? So I would like to make it similar to the way it's done in django-registration, where :
to, args, kwargs = backend.post_registration_redirect(request, new_user)
return redirect(to, *args, **kwargs)
def post_registration_redirect(self, request, user):
return ('registration_complete', (), {})
Ok so now, can I call directly my view function or do I need to provide a url for it ? And what more important, how my funciotn call (and a url if needed) should look like ? Both backend, and cleaned_data are just passed through this view for a later usage. I've tried this, but it's improper :
url(r'^link/$', some-view-name)
def some-view-name(request, *args):
As well as this :
return redirect('some_url', backend=backend, dataform.cleaned_data)
url(r'^link/$', some-view-name)
def some-view-name(request, backend, data):
still NoReverseMatch . But in django-registration, I've seen something like this :
url(r'^register/$',register,{'backend': 'registration.backends.default.DefaultBackend'}, name='registration_register'),
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='user/login_logout_register/registration_form.html',
extra_context=None):
urls.py:
#...
url(r'element/update/(?P<pk>\d+)/$', 'element.views.element_update', name='element_update'),
views.py:
from django.shortcuts import redirect
from .models import Element
def element_info(request):
# ...
element = Element.object.get(pk=1)
return redirect('element_update', pk=element.id)
def element_update(request, pk)
# ...
Firstly, your URL definition does not accept any parameters at all. If you want parameters to be passed from the URL into the view, you need to define them in the urlconf.
Secondly, it's not at all clear what you are expecting to happen to the cleaned_data dictionary. Don't forget you can't redirect to a POST - this is a limitation of HTTP, not Django - so your cleaned_data either needs to be a URL parameter (horrible) or, slightly better, a series of GET parameters - so the URL would be in the form:
/link/mybackend/?field1=value1&field2=value2&field3=value3
and so on. In this case, field1, field2 and field3 are not included in the URLconf definition - they are available in the view via request.GET.
So your urlconf would be:
url(r'^link/(?P<backend>\w+?)/$', my_function)
and the view would look like:
def my_function(request, backend):
data = request.GET
and the reverse would be (after importing urllib):
return "%s?%s" % (redirect('my_function', args=(backend,)),
urllib.urlencode(form.cleaned_data))
Edited after comment
The whole point of using redirect and reverse, as you have been doing, is that you go to the URL - it returns an Http code that causes the browser to redirect to the new URL, and call that.
If you simply want to call the view from within your code, just do it directly - no need to use reverse at all.
That said, if all you want to do is store the data, then just put it in the session:
request.session['temp_data'] = form.cleaned_data
I do like this in django3
redirect_url = reverse('my_function', args=(backend,))
parameters = urlencode(form.cleaned_data)
return redirect(f'{redirect_url}?{parameters}')
I am new to Django. One of my project, I used render instead of redirect to send data. That worked good. My code was like this --->
for key, value in request.POST.lists():
print(key, value)
if key.split('-')[-1] != 'csrfmiddlewaretoken':
qu_id = key.split('-')[-1]
get_answer = Answer.objects.filter(question_id=qu_id,
correct_answer__option__contains=value[0])
total_correct_answer = get_answer.count()
context = {'score': total_correct_answer}
return render(request, 'result.html', context)
context = {'questions': questions, 'total_correct_answer': total_correct_answer}
return render(request, 'test.html', context)
class GuideForms(FlaskForm):
FileField = FileField('FileField')
FileFiled_hidden = HiddenField()
text = HiddenField()
text.data = url_for('admin.gen')
The code above result in this error:
"Attempted to generate a URL without the application context being"
RuntimeError: Attempted to generate a URL without the application context being pushed. This has to be executed when application context is available.
Is there a way to define the fixed value of a field (text), as a url_for?
As I see, python generates its forms before its routes. So its basically impossible to define a form value as a url_for('blueprint.route').
The workaround you must do is:
Set a value for form.field.data when the form is used in a route.
#blueprint.route("/upload")
def upload():
form = GuideForms()
form.text.data = url_for('admin.gen')
You can do this when creating an instance of this form,
try,
class GuideForms(FlaskForm):
FileField = FileField('FileField')
FileFiled_hidden = HiddenField()
text = HiddenField()
def __init__(self, , *args, **kwargs):
super(GuideForms, self).__init__(*args, **kwargs)
text.data = url_for('admin.gen')
And make sure you create an instance of this form when there is application context
I am using a custom python framework, (not django or webapp2), following this instructions: Create an upload URL, I have been available to upload the file, basically I submit my form to an URL provided by a script with this:
from google.appengine.ext import blobstore
upload_url = blobstore.create_upload_url('/upload')
By checking the blobstore viewer I notice that the file has been uploaded, this brings my first question.
How to filter, before uploading? for example, how to upload only if an image type is a jpg or png and discard anything else.
My second question is, how to properly handle the the uploaded file, what I found so far is to create a custom get_uploads method, this is what I am using but wondering if is the right way:
import cgi
from google.appengine.ext import blobstore
class APIResource(object):
def get_uploads(self, request, field_name=None):
if hasattr(request, '__uploads') == False:
request.environ['wsgi.input'].seek(0)
fields = cgi.FieldStorage(
request.environ['wsgi.input'],
environ=request.environ)
request.__uploads = {}
for key in fields.keys():
field = fields[key]
if isinstance(field, cgi.FieldStorage):
if 'blob-key' in field.type_options:
request.__uploads.setdefault(
key, []).append(
blobstore.parse_blob_info(field))
if field_name:
try:
return list(request.__uploads[field_name])
except KeyError:
return []
else:
results = []
for uploads in request.__uploads.itervalues():
results.extend(uploads)
return results
def dispatch(self, request, response):
blob_info = self.get_uploads(request, 'picture')[0]
print blob_info, blob_info.key(), request.__uploads
I would like to avoid uploading first, later check if type is valid and delete if not, so that I can not fill my storage with garbage in case I can't handle/get the upload key.
Any ideas ?
HI,
I want to validate my urls whether they are post or get with caring the proper data.So i want to validate these urls before they call to respective views.So i am willing to write the some kind of middleware between view and urls so that i can keep safe the system.I am not aware how do i pass the data through middleware code to view.In middle ware i will write the unittest code.which will validate the urls if valid then will pass to the respective view other wise happy to say 404 .So can any buddy suggest me how do i handle the case.Or may be their is another alternative best way to do this validation.
Thanks to all.
You should really be checking for request type in your views, and not in a middleware. As I mentioned in the comments above, you can't tell whether a request is a POST message from the URL alone, let alone determine what POST data it carries.
Checking the request type within a view is very straight-forward -- simple check that request.method is equal to "GET" or "POST".
If you're doing this often, a short cut would be to create a decorator which does this check for you. For example, the following decorator checks that a GET request was used to call this view, or else return an HttpResponseBadRequest object (status code 400):
# untested code, use with care
def require_GET(view_func):
def wrap(request, *args, **kwargs):
if request.method != "GET":
return HttpResponseBadRequest("Expecting GET request")
return view_func(request, *args, **kwargs)
wrap.__doc__ = view_func.__doc__
wrap.__dict__ = view_func.__dict__
wrap.__name__ = view_func.__name__
return wrap
You can then simply prepend your view function with #require_GET and the check will be done whever the view is called. E.g.
#require_GET
def your_view(request):
# ...
You can do the same for POST.
Here's an example decorator checking for POST request which takes an optional list of fields that must be provided with the POST request.
# again, untested so use with care.
def require_POST(view_func, required_fields=None):
def wrap(request, *args, **kwargs):
if request.method != "POST":
return HttpResponseBadRequest("Expecting POST request")
if required_fields:
for f in required_fields:
if f not in request.POST:
return HttpResponseBadRequest("Expecting field %s" % f)
return view_func(request, *args, **kwargs)
wrap.__doc__ = view_func.__doc__
wrap.__dict__ = view_func.__dict__
wrap.__name__ = view_func.__name__
return wrap
Use like this:
#require_POST
def another_view(request):
# ...
or:
#require_POST(required_fields=("username", "password"))
def custom_login_view(request):
# ...
Update
OK, my bad. I've just reinvented wheel.
Django already provides the #require_GET and #require_POST decorators. See django.views.decorators.http.
Like others said, you must do it in your view, or maybe you must say what you are trying to do for the best...
Anyway, you can not create a responce object in process_request , you can only add variables or change variables on the related request, like the sessionid variable used by django, or any such thing... Or update any existing request variables...
So, you must use process_view, which is triggered after process_request and just before your related view function is executed.Since you have request object at hand, you can check GET or POST data by using request.GET or request.POST.
For doing this, you must add your middle class to MIDDLEWARE_CLASSES in settings.py and write a proper middleware process_view function. For writing middlewares see middleware documentation and check existing middlewares of django. Or tell me what you are rtying to do...
In my view function I want to call another view and pass data to it :
return redirect('some-view-name', backend, form.cleaned_data)
, where backend is of registration.backends object, and form.cleaned_data is a dict of form data (but both must be either sent as *args or **kwargs to prevent raising Don't mix *args and **kwargs in call to reverse()! error). From what I've found in the docs :
def my_view(request):
...
return redirect('some-view-name', foo='bar')
It looks like I need to provide 'some-view-name' argument, but is it just the name of the view function, or the name of the url ? So I would like to make it similar to the way it's done in django-registration, where :
to, args, kwargs = backend.post_registration_redirect(request, new_user)
return redirect(to, *args, **kwargs)
def post_registration_redirect(self, request, user):
return ('registration_complete', (), {})
Ok so now, can I call directly my view function or do I need to provide a url for it ? And what more important, how my funciotn call (and a url if needed) should look like ? Both backend, and cleaned_data are just passed through this view for a later usage. I've tried this, but it's improper :
url(r'^link/$', some-view-name)
def some-view-name(request, *args):
As well as this :
return redirect('some_url', backend=backend, dataform.cleaned_data)
url(r'^link/$', some-view-name)
def some-view-name(request, backend, data):
still NoReverseMatch . But in django-registration, I've seen something like this :
url(r'^register/$',register,{'backend': 'registration.backends.default.DefaultBackend'}, name='registration_register'),
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='user/login_logout_register/registration_form.html',
extra_context=None):
urls.py:
#...
url(r'element/update/(?P<pk>\d+)/$', 'element.views.element_update', name='element_update'),
views.py:
from django.shortcuts import redirect
from .models import Element
def element_info(request):
# ...
element = Element.object.get(pk=1)
return redirect('element_update', pk=element.id)
def element_update(request, pk)
# ...
Firstly, your URL definition does not accept any parameters at all. If you want parameters to be passed from the URL into the view, you need to define them in the urlconf.
Secondly, it's not at all clear what you are expecting to happen to the cleaned_data dictionary. Don't forget you can't redirect to a POST - this is a limitation of HTTP, not Django - so your cleaned_data either needs to be a URL parameter (horrible) or, slightly better, a series of GET parameters - so the URL would be in the form:
/link/mybackend/?field1=value1&field2=value2&field3=value3
and so on. In this case, field1, field2 and field3 are not included in the URLconf definition - they are available in the view via request.GET.
So your urlconf would be:
url(r'^link/(?P<backend>\w+?)/$', my_function)
and the view would look like:
def my_function(request, backend):
data = request.GET
and the reverse would be (after importing urllib):
return "%s?%s" % (redirect('my_function', args=(backend,)),
urllib.urlencode(form.cleaned_data))
Edited after comment
The whole point of using redirect and reverse, as you have been doing, is that you go to the URL - it returns an Http code that causes the browser to redirect to the new URL, and call that.
If you simply want to call the view from within your code, just do it directly - no need to use reverse at all.
That said, if all you want to do is store the data, then just put it in the session:
request.session['temp_data'] = form.cleaned_data
I do like this in django3
redirect_url = reverse('my_function', args=(backend,))
parameters = urlencode(form.cleaned_data)
return redirect(f'{redirect_url}?{parameters}')
I am new to Django. One of my project, I used render instead of redirect to send data. That worked good. My code was like this --->
for key, value in request.POST.lists():
print(key, value)
if key.split('-')[-1] != 'csrfmiddlewaretoken':
qu_id = key.split('-')[-1]
get_answer = Answer.objects.filter(question_id=qu_id,
correct_answer__option__contains=value[0])
total_correct_answer = get_answer.count()
context = {'score': total_correct_answer}
return render(request, 'result.html', context)
context = {'questions': questions, 'total_correct_answer': total_correct_answer}
return render(request, 'test.html', context)