Given a URL for example:
url(r'^article/(?P<article_id>\d+)/review/(?P<review_id>\d+)/rate/$', views.rate_reviewer,
name='review_rate_reviewer')
Is there a way in django that I could get a list of the Kwargs this URL required eg. ['article_id', 'review_id']?
Your view function signature should be:
def rate_reviewer(request, article_id, review_id):
#custom logic where you can use article_id and review_id
rate_article(article_id, review_id)
Other than that you could of course request url like:
http:localhost:8000/review_article?article_id=7&review_id=15....
and inside view function access these arguments with
def rate_reviewer(request):
article_id=request.GET.get("article_id",None)
review_id = request.GET.get("review_id", None)
You can access all arguments in GET request with request.GET (in POST request it's request.POST), and here you will get dictionary with all input parameters.
Related
TLDR: I want to be able to provide slug in reverse_lazy('view', kwargs={'slug':'my_page'}) like this: reverse_lazy('view').apply(kwargs={'slug':'my_page'}), after creating the lazy object.
I have the following url pattern that includes a slug to identify a page model instance:
url(r'^(?P<slug>'+settings.SLUG_PATTERN+')/$', views.MyView.as_view(), name='view'),
I have another view for editing the page:
url(r'^(?P<slug>'+settings.SLUG_PATTERN+')/_edit/$',
views.MyEditView.as_view(success_url=reverse_lazy('view')), name='edit'),
Note the addition of success_url so that when I submit the form with the new content I'm redirected to the now-edited page. In case I ever change my view url pattern I don't have to worry about updating the redirect for my edit url.
After the form is validated and saved, the view grabs the success url to be used in a HttpResponseRedirect. However just the name 'view' isn't enough to identify the URL. I also need to know the slug name which is stored in my page model's slug field.
A similar question is here: success_url in UpdateView, based on passed value
The answers suggest writing a custom get_success_url for every view, but there must be better approaches.
In the generic views in django's edit.py there's this:
url = self.success_url.format(**self.object.__dict__)
If success_url were given as a hard coded URL but with a slug identifier such as '{slug}/' this would replace it with the slug field in my model. That's very close to what I want, but I don't want to hard code my URL. This brings me to my question:
How can I pass in parameters to a reverse_lazy object? I would use this in my base view's get_success_url with self.object.__dict__ and it'd just work everywhere.
Moreover if my slug string was stored on separate Slug model I might want the success URL to be '{slug.name}/'. With the above approach I could supply a mapping between the URL parameters and model attributes:
redirect_model_mapping = {'slug': '{slug.name}'}
...
def get_success_url(self):
url = self.success_url
if is_a_lazy_redirect(url):
url = url.somehow_apply_parameters(redirect_model_mapping)
return url.format(**self.object.__dict__)
I would like somehow_apply_parameters to be equivalent to originally calling reverse_lazy('blog:view', kwargs=redirect_model_mapping). However I don't think this should be in urls.py because it shouldn't have to know about the mapping.
This is a hack, but does what I want...
class MyView(FormMixin, ...):
#this is actually set on child classes
redirect_model_mapping = {'slug':'{slug.name}'}
def get_success_url(self):
url = self.success_url
if url is not None:
if hasattr(self.success_url, '_proxy____kw'):
url_parameters = dict((k, v.format(**self.object.__dict__)) for k, v in six.iteritems(self.redirect_model_mapping))
url._proxy____kw = {'kwargs': url_parameters}
url = force_text(url)
else:
url = url.format(**self.object.__dict__)
else:
raise ImproperlyConfigured("No URL to redirect to.")
return url
It replaces the kwards parameter normally passed to reverse_lazy but after it actually has the values it needs. As reverse_lazy also requires the string to match the regex, I had to make the mapping between url parameters and the values in the models first.
I'd quite like an approach that doesn't need to write to _proxy____kw.
I have a paginated comments page. Pagination is provided by django-endless-pagination. When clicking to new paginated pages a get parameter is append to the url. Such as ?page=4.
Each comment on each paginated page displays a 'reply to' comment form containing a captcha field.
My view uses CreateView and I implement form_invalid myself in order to add some data to the context variable. At the end of my form_invalid method I return self.render_to_response(context)
The Problem
If a user attempts to reply to a comment when on page 4, and that user supplies and invalid captcha, then the pagination get parameter (?page=4) is lost during the response.
How can I redirect to the full path (keeping get params) and pass context data along with it?
Thanks
This problem is similar to this SO question, but my scenario is a little different given that I want to maintain my form state (the captcha error mentioned in question) in the redirect.
Thank you #petkostas for pointing out HTTP_REFERER
My resolution to this involved storing the context in cache with a cache key derived from the current timestamp. I redirect to the (same) comments url, but in doing this I append the current comment page as a get parameter and also the timestamp as another get parameter.
Then during get requests the view checks for the existence of the timestamp parameter. If it is provided, then a cache.get call is made to retrieve the needed context data. And finally, the cached item is deleted.
from datetime import datetime
from django.core.cache import cache
from django.shortcuts import redirect
from django.utils.dateformat import format
class MyView(CreateView):
def form_invalid(self, form, **kwargs):
context = {
'form': form,
... other context stuff ...
}
timestamp = format(datetime.now(), u'U')
cache.set('invalid_context_{0}'.format(timestamp), context)
page = 1
full_path = self.request.META.get('HTTP_REFERER')
if '?' in full_path:
query_string = full_path.split('?')[1].split('&')
for q in query_string:
if q.startswith('page='):
page = q.split('=')[1]
response = redirect('my_comments_page')
response['Location'] += '?page={0}&x={1}'.format(page, timestamp)
return response
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context.update({
... add context stuff ...
})
if 'x' in self.request.GET:
x = self.request.GET.get('x')
cache_key = 'invalid_context_{0}'.format(x)
invalid_context = cache.get(cache_key)
if invalid_context:
context.update(invalid_context)
cache.delete(cache_key)
return context
I haven't used django-endless-pagination, but Django offers 2 ways to get either the full current request path, or the Refferer of the request:
In your view or template (check documentation on how to use request in templates) for the referring requesting page:
request.META.get('HTTP_REFERER')
Or for the current request full path:
request.full_path()
Below is the code from views.py where I am using render_to_response to direct the user to done.html along with a dictionary of variables. But, I actually want to direct the user to a url /home_main/#signin_completeand pass the dictionary of variables that are callable. Can someone please suggest if there is a way of doing this ?
def done(request):
"""Login complete view, displays user data"""
scope = ' '.join(GooglePlusAuth.DEFAULT_SCOPE)
return render_to_response('done.html', {
'user': request.user,
'plus_id': getattr(settings, 'SOCIAL_AUTH_GOOGLE_PLUS_KEY', None),
'plus_scope': scope
}, RequestContext(request))
EDIT
My requirement is to render a second page (signin_complete) from a multipage html (home_main.html). Currently, I am achieving this by redirecting the user with HttpResponseRedirect as shown below. But, I would also like to pass a callable dictionary that I can use in the second page of the multipage html.
Here is a link that gives more information of a multipage html under multipage template structure.
def done(request):
"""Login complete view, displays user data"""
scope = ' '.join(GooglePlusAuth.DEFAULT_SCOPE)
return HttpResponseRedirect('/home_main/#signin_complete')
Below is the dictionary that I would like to pass to the second page (sign_complete) in the multi page html.
{
'user': request.user,
'plus_id': getattr(settings, 'SOCIAL_AUTH_GOOGLE_PLUS_KEY', None),
'plus_scope': scope
}
The session is the place to store data between requests.
# in done():
request.session['dict_to_save'] = my_dict_to_save
return redirect('/new/url/to/redirect/to')
# in the new view:
values_from_session = request.session.pop('dict_to_save', None)
It would be much better if you would redirect request inside done() method, like the docs advises you to do.
This solves your issue as well, since you can define your own url to redirect to, there's related SO question of how to add hash tags when redirecting.
https://xxxx/category_check_view/?item_id=2
Above is a sample of URL pattern. How should i configured my URL in order to enable it to redirect to the right view?
I seem to get it working for a url like this https://xxxx/category_check_view/2/ only so far.
You can pass parameters to a view either in the url:
/category_check_view/2
Or via GET params:
/category_check_view/?item_id=2
GET params are not processed by the URL handler, but rather passed directly to the GET param dict accessible in a view at request.GET.
The Django (i.e. preferred) way to do handle URLs is the first one. So you would have a URL conf:
(r'^category_check_view/(\d{4})$', 'proj.app.your_view'),
And a matching view:
def your_view(request, id):
obj = Obj.objects.get(id=id)
# ...
However, if you insist on passing the param via GET you would just do:
(r'^category_check_view$', 'proj.app.your_view'),
And:
def your_view(request):
id = request.GET.get('item_id')
obj = Obj.objects.get(id=id)
# ...
You can't use get parameters in URL pattern. Use them in your view:
item_id = request.GET.get('item_id')
url scheme sample:
http://domain/tests/?_=1111111&data=3333333&status=22222222
In view, I need data and status, any approach is welcome! All the params are integers.
Your data passed as GET parameters does not need to be matched in urls file.
urlpatterns += patterns(
('^tests/$', 'app.views.test'),
)
You will have that data in your view as: request.GET.get('_', None), ...
Your can write a form which will help you to validate and clean up the data and use it like this in your view:
form = some_form(data=request.GET)
if not form.is_valid():
raise InvalidRequest()
data = form.cleaned_data
If that's the url the params are accessible through the request.GET dictionary in the view method.
def my_view(request):
print request.GET['data']
Of course if you need to validate whether the elements are in the dictionary('data' in request.GET) and convert them to int prior using them like that. int(request.GET['data'])