Now I want to reuse the post method of BundleList. Either I find out the absolute URL and use requests.post(URL) to send a request.
The 2nd way is to reuse by return BundleList.as_view()(request) in a view function. But I can't set request.data = data. Request data is immutable.
When I try to use
url = reverse_lazy(BundleList.as_view(), request=request)
print(f"{url = }")
It just gives me:
NoReverseMatch at /generateSampleBundle/
Reverse for 'my_app.views.BundleList' not found. 'my_app.views.BundleList' is not a valid view function or pattern name.
The BundleList is a class-based view with get and post method.
drfurlpatterns = [ # DRF URL endpoints
path('bundles/', views.BundleList.as_view()),
]
Can anyone help me out?
You should set name for your view and use that name in reverse_lazy()
for ex:
drfurlpatterns = [ # DRF URL endpoints
path('bundles/', views.BundleList.as_view()), name='bundle-list'
]
then
url = reverse_lazy('bundle-list', request=request)
see, docs
Also, to know why request.data is sometimes immutable see this question
Related
I am trying to use redirect with context but I get a NoReverseMatch exception. Code is as follows:
return redirect(reverse('td:success', kwargs={ 'data': my_data }))
When I use render, all goes well and I am able to access the context in the template. But I want redirect instead.
enter image description here
But I want redirect instead.
You can't. The redirect(…) function [Django-doc] simply returns a HTTP response that contains the URL to visit next. The browser will then visit that URL, it is thus the browser that makes the next HTTP request, and the previous request and response are "destroyed". You thus need the database, cookies, session variables, URL parameters, etc. to pass data between the two requests.
If you use a URL parameter, you can thus add data to this. In that case, your code snippet can be simplified to:
return redirect('td:success', data=my_data)
there is no need to work with reverse(…) since redirect(…) will call reverse(…) internally.
Since Django 1.5 raw post data is accessible via request.body.
In my application I sometimes get data send via a form and sometimes raw data (json for example).
Is there any way to write a function like this that does not fail?
def get_post_var(request, name):
result = request.POST.get(name)
if result:
return result
post_body = dict(urlparse.parse_qsl(request.body))
result = post_body.get(name)
if result:
return result
return None
Use request.data instead of request.body.
request.data does not read the data stream again.
The error You cannot access body after reading from request's data stream will be triggered on a request if (1) that request method is POST, (2) that request's POST dictionary is accessed in middleware, in either process_request or process_view and (3) within the view function, request.body is accessed. It is on (3) that the error will be raised, even though the real cause of the bug is (2).
In order to resolve the error, you need to examine your middleware for where it accesses request.POST and modify it such that it doesn't access request.POST anymore.
The Django docs say that middleware should not access request.POST, and this is one consequence of ignoring that recommendation.
Also check out this Django ticket on the issue, which includes the note:
[M]iddleware that hits request.POST should (usually) be considered a
bug. It means that the view will be unable to set any custom upload
handlers, perform custom parsing of the request body, or enforce
permission checks prior to file uploads being accepted.
Adding to Adam Easterling's answer it is worth noting that Django itself 'violates' the hint of not using request.POST in middleware:
The CsrfViewMiddleware class can be considered an exception, as it
provides the csrf_exempt() and csrf_protect() decorators which allow
views to explicitly control at what point the CSRF validation should
occur.
Which does not sanitilize the violation IMO
For those interested to know, I faced this issue:
You cannot access body after reading from request's data stream
when I added 'oauth2_provider.contrib.rest_framework.OAuth2Authentication'
in the "REST_FRAMEWORK" like so in the settings.py:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
...
),
Of course disabling this will work but not a workaround I would be proud of.
I was able to read my request.POST after putting #csrf_exempt before my view function. Because CSRF middleware accesses POST data.
I found a trick, using for my middleware,
request._read_started = False
after doing that, ready body again and it works.
For those with the same error who are not readying the body or POST, I had this same error when I used this line of code in a process_view middleware::
event = request.event if 'event' in request else None
Solved by settings request.event = None at the top of the function so I could then use:
event = request.event
I want to redirect URL without slug, to the one with slug, at the urls.py level.
My endpoints looks as follows:
(r'/invoices/<:(-?\d+)>/print/', PrintHandler, 'ShortPrintHandler')
(r'/invoices/<:(-?\d+)>/<:([\w-]*)>/print/', PrintHandler, 'FullPrintHandler')
Is there any way I can pass first, decimal, argument from short URL to the long one, on redirect? Generating URLs without slug is already covered at handler level.
Tried to handle it with
RedirectRoute(r'/invoices/<:(-?\d+)>/print/', PrintHandler, redirect_to_name='FullPrintHandler')
But an error was thrown:
KeyError: 'Missing argument "1" to build URI.'
You can't do that with just a RedirectRoute; you need to get the slug value from somewhere.
You'll need to write a standard route, and in the handler you should get the object from the datastore and return a redirect to the full path using the slug.
Something like (untested):
class RedirectToFullPath(webapp2.RequestHandler):
def get(self, invoice_id):
invoice = Invoice.get_by_id(invoice_id)
self.redirect_to('FullPrintHandler', invoice_id, invoice.slug)
I'm using Django==1.10.5 and djangorestframework==3.5.3.
I have a few ModelViewSets that handle JSON API requests properly. Now, I'd like to use TemplateRenderer to add HTML rendering to those same ModelViewSets. I started with the list endpoint, and created a simple template that lists the available objects. I implemented the get_template_names to return the template I created.
Accessing that endpoint through the browser works fine when there are no objects to list, so everything related to setting up HTML renderers alongside APIs seems to work.However, when tere are objects to return the same endpoint fails with the following error:
ValueError: dictionary update sequence element #0 has length XX; 2 is required
Where XX is the number of attributes the object has.
This documentation section suggests the view function should act slightly differently when returning an HTML Response object, but I assume this is done by DRF's builtin views when necessary, so I don't think that's the issue.
This stackoverflow Q/A also looks relevant but I'm not quite sure it's the right solution to my problem.
How can I make my ModelViewSets work with both HTML and JSON renderers?
Thanks!
DRF has a brief explanation of how to do this in their documentation.
I think you'd do something like this...
On the client side, tell your endpoint what type of response you want:
fetch(yourAPIUrl, {
headers: {
'Accept': 'application/json'
// or 'Accept': 'text/html'
}
})
In your view, just check that and act accordingly:
class FlexibleAPIView(APIView):
"""
API view that can render either JSON or HTML.
"""
renderer_classes = [TemplateHTMLRenderer, JSONRenderer]
def get(self, request, *args, **kwargs):
queryset = Things.objects.all()
# If client wants HTML, give them HTML.
if request.accepted_renderer.format == 'html':
return Response({'things': queryset}, template_name='example.html')
# Otherwise, the client likely wants JSON so serialize the data.
serializer = ThingSerializer(instance=queryset)
data = serializer.data
return Response(data)
My views.py code:
from django.template import Context, loader, RequestContext
from django.http import HttpResponse
from skey import find_root_tags, count, sorting_list
from search.models import Keywords
def front_page(request):
if request.method == 'get' :
str1 = request.getvalue['word']
fo = open("xml.txt","r")
for i in range(count.__len__()):
file = fo.readline()
file = file.rstrip('\n')
find_root_tags(file,str1,i)
list.append((file,count[i]))
sorting_list(list)
for name, count in list:
s = Keywords(file_name=name,frequency_count=count)
s.save()
fo.close()
return HttpResponseRedirect('/results/')
else :
str1 = ''
list = []
template = loader.get_template('search/front_page.html')
c = RequestContext(request)
response = template.render(c)
return HttpResponse(response)
def results(request):
list1 = Keywords.objects.all()
t = loader.get_template('search/results.html')
c = Context({'list1':list1,
})
return HttpResponse(t.render(c))
#this for everyone.
the flow is this:
1) I run my app on the server .
2)It shows me the search page due to the else part of the view "def front_page(request)", now I want to execute the if part of the view "def front_page(request)" because I want to execute my python code written there and the redirected to the view "def results(request)", how can I do that ?
3) what should I mention in "action" of the front_page.html and in urls.py so that I can get back to the same view again. because I could'nt get back to the same view that I want it is repetitively showing me the same search page.Please help.
To enlarge upon the answer posted by #Barnaby....by using action='#' your form will be posted to the same url as the url used in the get request for the form.
Then in your view code, you have logic that says - if the request for this url is a GET request then do the work to configure the form, otherwise, you assume it is a POST and then you can handle the response.
Additionally I would advise that the your view explicitly checks that the request is a POST and if not make the assumption that it is a GET, rather than the other way around (as you have it), this is safer, as GET and POST are not the only request types, and you definitely need to know that you are dealing with a POST request if you want to deal with variables submitted in the POST request.
Hope that helps
Short answer: action="#". This is a HTML trick to post back to the current URL.
The general answer to how to reference a view in a template is to use the url tag. You may also want to consider using Django's forms functionality.