Django session not persisting with generic views - python

class IndexTemplateView(TemplateView):
'''Index TemplateView.'''
template_name = 'frontend/index.html'
def post(self, request, *args, **kwargs):
'''Manages credentials received for methods calling authentication.'''
bitrix24_domain = request.GET.get('DOMAIN')
request.session['bitrix24_domain'] = bitrix24_domain
print(request.session['bitrix24_domain']) # String is stored and printed to the screen.
return redirect('index')
# Bitrix24 sends credentials via POST right after GET request.
# CSRF protection would cause error in this case.
#csrf_exempt
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
class LoginTemplateView(TemplateView):
'''Login TemplateView.'''
template_name = 'frontend/login.html'
def get(self, request, *args, **kwargs):
'''Renders the login page.'''
redirect_uri = get_google_redirect_uri()
print(request.session.items()) # Returns empty session. The string was never saved.
return redirect(redirect_uri)
I've tried setting request.session.modified = True but it didn't work either. I really don't know why I can't store a string in session. I've also tried to store in self.request within the POST request, but without success, tried to store it in the dispatch() function, in the setup() function. Tried almost everything and I can't store a single piece of information in my session. I'm also using Django Rest framework.
Can anyone help me on how to use sessions with generic views?

Problem solved, session does not work with tunneling service as Ngrok.

Related

Override update method of UpdateAPIView

I need to do some actions, before calls update().
my code
class CarView(generics.UpdateAPIView):
permission_classes = (IsAdminUser,)
serializer_class = CarSerializer
def get_queryset(self):
return ...
def update(self, request, *args, **kwargs):
# some actions
super(CarView, self).update(request, *args, **kwargs)
But I'm getting an error
error message
Expected a Response, HttpResponse or HttpStreamingResponse to be
returned from the view, but received a <type 'NoneType'>
How can I fix that?
Like most Django views, your update method on the ViewSet should be returning a response. Right now you aren't returning anything, which is why Django is complaining about receiving NoneType (as that is the default return value).
The issue is coming from the last line of your update method, where you are calling the parent update but aren't returning it.
super(CarView, self).update(request, *args, **kwargs)
If you returned it, the response that came from the update method that is normally defined would be passed down the chain and rendered as you would expect.
return super(CarView, self).update(request, *args, **kwargs)
This is happening because you have not returned anything in your update method. Django views expect a Response object to be returned. Just add a return in your update method.
class CarView(generics.UpdateAPIView):
permission_classes = (IsAdminUser,)
serializer_class = CarSerializer
def get_queryset(self):
return ...
def update(self, request, *args, **kwargs):
# some actions
return super(CarView, self).update(request, *args, **kwargs)
According to docs,
REST framework supports HTTP content negotiation by providing a
Response class which allows you to return content that can be rendered
into multiple content types, depending on the client request.
The Response class subclasses Django's SimpleTemplateResponse.
Response objects are initialised with data, which should consist of
native Python primitives. REST framework then uses standard HTTP
content negotiation to determine how it should render the final
response content.
So, to render the data into different content types, you have to return a response.

Redirecting to external page from django class based view

I'm trying to redirect users based on the referer in the request header. Basically, if the referer is say https://www.google.com, I would like to send them to a page, not on my website. Otherwise, continue processing as usual.
Here is what I have so far
class ArticleAccess(TemplateView, SomeMixin):
http_method_names = ['get']
template_name = 'template.html'
def dispatch(self, request, *args, **kwargs):
return super(ArticleAccess, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(ArticleAccess, self).get_context_data(**kwargs)
item = get_object_or_404(ClientItem.objects.using(self.get_site().name), id=kwargs['article_id'])
if self.request.META.get('HTTP_REFERER') == 'https://www.google.com/':
return redirect(item.item_url)
context['id'] = item.id
context['name'] = item.name
context['html'] = item.description
context['item_url'] = item.item_url
return context
This just stays on the same page instead of redirecting. I have also tried HttpResponseRedirect, but to no avail
alecxe is correct.. you'd have to redirect from a method that is expected to return an HttpResponse.
get_context_data is not expected to return an HttpResponse and isn't ever returned by the view. It's always used to get a data dict to populate say a template. No matter what you return from this method, it will never override the response.
Therefore wherever you write this override, it needs to be in a place that is expected to return a response, such as get, post, dispatch.
The problem now is to determine how to get your object outside of the get_context_data method.
For debugging, I recommend you to start by just using a plain redirect('some_view') without conditions on your dispatch method so you can check if redirection is hit as expected and only then, go for conditions and anything else. #Yuji-tomita-tomita is just right! :) Django-pdb and ipdb are very nice tools.

Redirect using CBV's in Django

I believe this is a simple one, just can't spot the solution. I have a view that does a bit of work on the server then passes the user back to another view, typically the original calling view.
The way I'm rendering it now, the url isn't redirected, ie it's the url of the original receiving view. So in the case the user refreshes, they'll run that server code again.
class CountSomethingView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# so some counting
view = MyDetailView.as_view()
return view(request, *args, **kwargs)
I strongly recommend not overriding get or post methods. Instead, override dispatch. So, to expand on Platinum Azure's answer:
class CountSomethingView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self, **kwargs):
url = you_can_define_the_url_however_you_want(**kwargs)
return url
def dispatch(self, request, *args, **kwargs):
# do something
return super(CountSomethingView, self).dispatch(request, *args, **kwargs)
when a user does an action and I need to redirect him to the same page, first of all I use a templateView to display a simple "thanks" (for example) then provide a link to go back to the previous page with a simple {% url %}
for example :
from django.views.generic import CreateView, TemplateView
from django.http import HttpResponseRedirect
class UserServiceCreateView(CreateView):
form_class = UserServiceForm
template_name = "services/add_service.html"
def form_valid(self, form):
[...]
return HttpResponseRedirect('/service/add/thanks/')
class UserServiceAddedTemplateView(TemplateView):
template_name = "services/thanks_service.html"
def get_context_data(self, **kw):
context = super(UserServiceAddedTemplateView, self).\
get_context_data(**kw)
context['sentance'] = 'Your service has been successfully created'
return context
in the template thanks_service.html i use {% url %} to go back to the expected page
Hope this can help
Performing a redirect in a Django Class Based View is easy.
Simply do a return redirect('your url goes here').
However, I believe this isn't what you want to do.
I see you're using get().
Normally, when speaking about HTTP, a GET request is seldom followed by a redirect.
A POST request is usually followed by a redirect because when the user goes backwards you wouldn't want to submit the same data again.
So what do you want to do?
What I think you want to do is this:
def get(self, request, *args, **kwargs):
return render_to_response('your template', data)
or even better
def get(self, request, *args, **kwargs):
return render(request, self.template_name, data)
If you're creating or updating a model, consider inheriting from CreateView or UpdateView and specifying a success_url.
If you're really doing a redirect off of an HTTP GET action, you can inherit from RedirectView and override the get method (optionally also specifying permanent = False):
class CountSomethingView(LoginRequiredMixin, RedirectView):
permanent = False
def get(self, request, *args, **kwargs):
# do something
return super(CountSomethingView, self).get(self, request, *args, **kwargs)
Note that it's really bad practice to have a get action with side-effects (unless it's just populating a cache or modifying non-essential data). In most cases, you should consider using a form-based or model-form-based view, such as CreateView or UpdateView as suggested above.

Render Django view class to either string or response

I have a template that I want to be able to both serve directly and embed in arbitrary other templates in my Django application. I tried to create a view class for it that looks like this:
class TemplateView(View):
def get(self, request):
context = self._create_context(request)
return render_to_response('template.html', context)
def get_string(self, request):
context = self._create_context(request)
return render_to_string('template.html', context)
def _create_context(self, request):
context = {}
# Complex context initialization logic...
return context
I've wired get to my Django URLs. However, I haven't been able to figure out how to instantiate TemplateView so that I can call get_string from other views.
There must be a better way to go about doing this. Ideas?
Update: I've seen some folks talking about making a request internally and using response.content, which would save me from having to write the get_string method. So, perhaps a better question is: How do I make a request to TemplateView from another view?
I'd follow in django's CBV pattern: it determines via dispatch what method to return. By default based on request.method. Why not based on any other argument passed to dispatch()?
So subclass dispatch and give it a way to determine whether or not to return get_string.
def dispatch(self, request, *args, **kwargs):
if 'as_string' in kwargs:
return self.get_string(request)
return super(TemplateView, self).dispatch(request, *args, **kwargs)
response = TemplateView.as_view()(request, as_string=True)

Validating a form in a get request, how?

in my way of perfectionism, I'm here to ask more questions about the not-so-well-documented class-based views.
I spend like 5 hours learning about class-based views, lurking into the code and I got a question.
Maybe what I'm trying to do is stupid, and if so, just say that.
I will put a simple example:
class SearchFormView(FormView):
template_name = 'search/search.html'
form_class = SearchForm
def get(self, request, *args, **kwargs):
form = SearchForm(self.request.GET or None)
if form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return super(SearchFormView, self).get(request, *args, **kwargs)
This is a perfect valid class (it is, right?).
You have a form, and you make a GET request with a query parameter.
Works like a charm.
But lets imagine... I validate the query input to prevent some type of attack and I see that the query is malicious so I put a validation error.
With the old functions, I have a form instance (empty) and I put data in it and validation errors if needed. I always return that instance, if empty (first request) or if it filled with errors (the case of the malicious query).
The problem is with class-based views. In my get method I work with an extra instance of SearchForm so if I put validation stuff would be there and if I call get on the father it will use the instance on "form_class" that would be empty.
So, I think that there should be a way where I use the same form always, I mean: I call the request method, I pick the form_class (not create a new form), pass the data, validate and the father will return that form with the validation stuff.
Im not sure if I explained this correctly. So in short, Im creating a copy of the form in the get but I return the father get who have another copy that will be empty, so my when I display the template, there will be no errors because the form sended is empty.
Any ideas? Thanks.
Your problem is that super(SearchFormView, self).get(request, *args, **kwargs) renders its own form and own context. It's only a 3 line view function, so you should really be overriding what you need to change its behavior.
def get(self, request, *args, **kwargs):
form = SearchForm(self.request.GET or None)
if form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return self.render_to_response(self.get_context_data(form=form))
Update: alternate idea if you'd like to continue using the super call
def get(self, request, *args, **kwargs):
self.form = SearchForm(self.request.GET or None)
if self.form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return super(SearchFormView, self).get(request, *args, **kwargs)
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
return getattr(self, 'form', None) or form_class(**self.get_form_kwargs())
The problem appears to be the fact that Django class based views only populate the form kwargs if the HTTP method is POST or PUT:
class FormMixin(object):
def get_form_kwargs(self):
"""
Returns the keyword arguments for instanciating the form.
"""
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
I found this a bit peculiar also, since I have on occasion used a form in a GET request (eg. a "search" form), which needed to perform some basic validation. I just override the get_form_kwargs() method on such views, to also populate the kwargs['data'] item, even when the HTTP method is GET.

Categories