Django: how to pass request object from one view to another? - python

Is there a way to pass request object from one view to another? Or do I have to dissect the request into individual parts or use request.__dict__ and pass those?
Currenlty, because the request cannot be serialized, the below code:
def explorer(request):
collect_activities_init(request)
return render(request, 'explorer/explorer.html')
def collect_activities_init(request):
task = collect_activities.delay(request)
return JsonResponse({'status_url': reverse('explorer:collect_activities_status', kwargs={'task_id': task.id})})
#celery.task(bind=True)
def collect_activities(self, request):
api = StravaApi()
code = request.GET.get('code', '') # extract code from redirect URL query string
Will give:
WSGIRequest: GET
'/explorer/?state=&code=3ba4hgh5ad99ea3cf8e52d8&scope='>
is not JSON serializable
But I need to have the request data from the first function in the last one, because its code extracts some data from the request.

The problem you're having is not passing request between those functions, but rather that celery requires arguments passed to a celery task to be serialized.
The simple solution here would be to pull out the attributes you need from the request then pass those to the task.
So instead of
task = collect_activities.delay(request)
Do
code = request.GET.get('code', '')
task = collect_activities.delay(code)
Of course, you'll need to change the collect_activities task definition to accept the code as an argument, rather than a request object.

I found this: Django pass object from view to next for processing
The top answer uses sessions in order to pass the save the object and retrieve it later:
def first_view(request):
my_thing = {'foo' : 'bar'}
request.session['my_thing'] = my_thing
return render(request, 'some_template.html')
def second_view(request):
my_thing = request.session.get('my_thing', None)
return render(request, 'some_other_template.html', {'my_thing' : my_thing})

Related

Put Django view return data into response data

I have a function based view and it has many checks in it and returns data based on check results. So I don't want to wrap my data into Response object every time manually. Is there a way to put function return value into OK response data automatically in Django Rest Framework? For example to have a valid view like this:
#api_view(['GET'])
def pre_reg_countries(request):
if some_condition:
return data_1 # instead of returning Response(data=data_1)
else:
if another condition:
return data_2
else:
return []
P.S. The idea came to me from Java Spring framework.
If Django does not provide some built-in solution, a decorator like this can solve the problem:
def responsify(view_method):
def add_response(*args, **kwargs):
data = view_method(*args, **kwargs)
return Response(data)
return add_response

How should I continue a Python Social Auth Partial Pipeline

The application I am working has an overwritten endpoint for the Python Social Auth /complete/<backend>/ endpoint.
within our urls.py:
urlspatterns = [
...
# Override of social_auth
url(r'^api/v1/auth/oauth/complete/(?P<backend>[^/]+)/$',
social_auth_complete,
name='social_complete'),
...
]
within views.py:
from social_django.views import complete
def social_auth_complete(request, backend, *args, **kwargs):
"""Overwritten social_auth_complete."""
# some custom logic getting variables from session (Unrelated).
response = complete(request, backend, *args, **kwargs)
# Some custom logic adding args to the redirect (Unrelated).
We are attempting to implement a partial pipeline method. The first time the endpoint is called everything works as expected.
#partial
def required_info(strategy, details, user=None, is_new=False, *args, **kwargs):
"""Verify the user has all the required information before proceeding."""
if not is_new:
return
for field in settings.SOCIAL_USER_REQUIRED_DATA:
if not details.get(field):
data = strategy.request_data().get(field)
if not data:
current_partial = kwargs.get('current_partial')
social_provider = kwargs.get('backend')
return strategy.redirect(f'.../?partial_token={partial_token}&provider={social_provider}'
else:
details[field] = data
This redirects the user to the front end in which they fill out a form which calls a POST request to orginal API api/v1/auth/oauth/complete/(?P<backend>[^/]+)/ with the following in the data:
{
'required_fieldX': 'data',
...
'partial_token': '',
}
Key Issues
Two things go wrong; When I pdb into required_info there is never any data within strategy.request_data(). There is still data within the kwargs['request'].body and I can take the data out there.
However
But I am afraid that the second time around we never get into this block of code from social-core:
partial = partial_pipeline_data(backend, user, *args, **kwargs)
if partial:
user = backend.continue_pipeline(partial)
# clean partial data after usage
backend.strategy.clean_partial_pipeline(partial.token)
else:
user = backend.complete(user=user, *args, **kwargs)
I know this to be true because when I interrogate the database the original Partial object still exists as if backend.strategy.clean_partial_pipeline(partial.token) was never called.
Final Questions
Why is the social_django.views.complete not processing the POST request as expected and as it appears to be in all the example applications. Is there an issue from our overwriting it? Should I just create a separate endpoint to handle the POST request and if so how do mimic all that goes on within #psa such that I can call backend.continue_pipeline(partial)?
I think there's only one issue here, and that's that the Django strategy doesn't look into request.body when loading the request data, you can see the method in charge here. There you can see that it looks for request.GET and/or request.POST, but not body.
You can easily overcome this by defining your custom strategy that extends from the built-in one, and override the request_data method to look for the values in request.body. Then define the SOCIAL_AUTH_STRATEGY to point to your class.

Get the Flask view function that matches a url

I have some url paths and want to check if they point to a url rule in my Flask app. How can I check this using Flask?
from flask import Flask, json, request, Response
app = Flask('simple_app')
#app.route('/foo/<bar_id>', methods=['GET'])
def foo_bar_id(bar_id):
if request.method == 'GET':
return Response(json.dumps({'foo': bar_id}), status=200)
#app.route('/bar', methods=['GET'])
def bar():
if request.method == 'GET':
return Response(json.dumps(['bar']), status=200)
test_route_a = '/foo/1' # return foo_bar_id function
test_route_b = '/bar' # return bar function
app.url_map stores the object that maps and matches rules with endpoints. app.view_functions maps endpoints to view functions.
Call match to match a url to an endpoint and values. It will raise 404 if the route is not found, and 405 if the wrong method is specified. You'll need the method as well as the url to match.
Redirects are treated as exceptions, you'll need to catch and test these recursively to find the view function.
It's possible to add rules that don't map to views, you'll need to catch KeyError when looking up the view.
from werkzeug.routing import RequestRedirect, MethodNotAllowed, NotFound
def get_view_function(url, method='GET'):
"""Match a url and return the view and arguments
it will be called with, or None if there is no view.
"""
adapter = app.url_map.bind('localhost')
try:
match = adapter.match(url, method=method)
except RequestRedirect as e:
# recursively match redirects
return get_view_function(e.new_url, method)
except (MethodNotAllowed, NotFound):
# no match
return None
try:
# return the view function and arguments
return app.view_functions[match[0]], match[1]
except KeyError:
# no view is associated with the endpoint
return None
There are many more options that can be passed to bind to effect how matches are made, see the docs for details.
The view function can also raise 404 (or other) errors, so this only guarantees that a url will match a view, not that the view returns a 200 response.
In addition to the #davidism answer (The core developer of flask).
Note that if you want to discover the view function of the current url processed by flask app. You can use the Request object of flask which :
The request object used by default in Flask. Remembers the
matched endpoint and view arguments.
and the Flask.view_functtions which:
#: A dictionary mapping endpoint names to view functions.
#: To register a view function, use the :meth:route decorator.
def get_view_function():
if request.url_rule:
return current_app.view_functions.get(request.url_rule.endpoint, None)

Should I use GET or POST to retrieve results Django

I'm using Django and have a view to get/insert some records in database. Here's my code:
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
#api_view(('GET','POST'))
#renderer_classes((TemplateHTMLRenderer,))
#csrf_exempt
def cours_list(request):
if request.method == 'GET':
data = JSONParser().parse(request)
results = Lesson.objects.get(discipline=data.discipline)
return Response({'cours': results}, template_name='search_results.html')
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = LessonSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
So to get data I use GET and to insert a new record I use POST. First, is this the right way to do ? I read once that it't a bad idea to use GET whatever the situation.
Also, the GET code is not actually working (I'm getting a bad request error) and it seems that this is comming from JSONParser that is not able to parse the request.
Edit1
Here is the request that is sent:
GET http://127.0.0.1:8000/cours/?{%22discipline%22:%22Mathematiques%22,%22localisation%22:%22%22}
Edit2
I tried to print requet.GET, and this what it gives:
<QueryDict: {'{"discipline":"Mathematiques","localisation":""}': ['']}>
When trying to access request.data['discipline'], it says:
django.utils.datastructures.MultiValueDictKeyError: "'discipline'"
Should I use GET or POST to retrieve results Django
To retrieve use a GET
I read once that it't a bad idea to use GET whatever the situation
Not at all, as long as your GET operation contains no side effects (e.g. in your GET method you are only reading from the database.
The GET code is not actually working (I'm getting a bad request error)
data2 = JSONParser().parse(request) # you used data2
results = Lesson.objects.get(discipline=data.discipline) # and you used data
^
|____ data? data2?
You're not passing the GET parameters correctly
You need to pass GET params like this url/?param1=value1
To read that value you use param1 = request.GET['param1'] and param1 will be the string value1
What you are doing isn't passing a value:
GET http://127.0.0.1:8000/cours/?{%22discipline%22:%22Mathematiques%22,%22localisation%22:%22%22}
That's why you get an empty value ['']
<QueryDict: {'{"discipline":"Mathematiques","localisation":""}': ['']}>
You can pass the entire data as a JSON string if you want, but I prefer to break it down like this:
url/?discipline=Mathematiques&localisation=Quelquechose
with
discipline = request.GET['discipline']
localisation = request.GET['localisation']
That's absolutely normally to use GET method for information retrieving, see HTTP/1.1: Method Dediniton, 9.3:
The GET method means retrieve whatever information (in the form of an
entity) is identified by the Request-URI. If the Request-URI refers to
a data-producing process, it is the produced data which shall be
returned as the entity in the response and not the source text of the
process, unless that text happens to be the output of the process.
For POST method, it's a good practice to submit new data with POST request, see HTTP/1.1: Method Dediniton, 9.5:
The POST method is used to request that the origin server accept the
entity enclosed in the request as a new subordinate of the resource
identified by the Request-URI in the Request-Line. POST is designed to
allow a uniform method to cover the following functions:
................................................................
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
- Extending a database through an append operation.
Also, take a look at Which HTTP methods match up to which CRUD methods? it will give you more clear idea of how and when particular HTTP methods shall be used and help you in next further development.

Django middleware process_template_response method not being called

I have the following middleware class:
class CommonContextMiddleware:
def process_template_response(self, request, response):
# Get the context and top videos
context = response.context_data
...
# Add most_recent and most_viewed to the context...
context['most_recent'] = top_videos['most_recent'][:3]
context['most_viewed'] = top_videos['most_viewed'][:3]
# ...then continue rendering
return response
However, no matter what I put in the function, it's never being called. I presumed that this method would be called for every single template response generated, am I wrong?
Thanks in advance.
I assume when you're talking about "template response", you are actually returning a TemplateResponse from your Django view?
This isn't really the best place for this sort of thing. If you just want to add variables into every template context, the best place to do it is in a context processor.

Categories