Django URL configuration - regex

I have a purchase page, it can take an optional argument as a gift, if it is a gift, the view passes a gift form to the template and if not, a regular purchase form.
my old regular url, which redirects to two seperate views:
(r'^(?P<item>[-\w]+)/purchase/$', 'purchase_view'),
(r'^(?P<item>[-\w]+)/purchase/gift$', 'gift_view'),
and the views was like this:
def purchase_view(request,item):
....use purchase form
def gift_view(request,item):
....use giftform
It is a bad design indeed, as both views having are almost everything same but the forms used.
I have also thougt about using GET and giving gift as a GET param however it wasnt a good idea as I am using POST method for these pages, especially would cause issue after validation.
How can I make this a single url and a single view?
Thanks

urls.py
url(r'^(?P<item>[-\w]+)/purchase/$', 'purchase_view', name='purchase_view'),
url(r'^(?P<item>[-\w]+)/purchase/(?P<gift>gift)/$', 'purchase_view', name='gift_view'),
views.py
def purchase_view(request, item, gift=False):
if gift:
form = GiftForm
else:
form = PurchaseForm
...

Related

How to Django reverse to pages created in Wagtail?

I have a Wagtail model for an object.
class ObjectPage(Page):
# fields, not really important
I want to make the object editable by front-end users, so I have also created a generic update view to accomplish this. My question is how I can use Django's reverse() function to point to the edited object in my get_success_url() method:
class EditObjectPage(LoginRequiredMixin, UpdateView):
# model, template_name, and fields defined; not really important
def get_success_url(self):
return("ObjectPage", kwargs={"slug" : self.object.slug}) # doesn't work
return("object_page_slug", kwargs={"slug" : self.object.slug}) # also doesn't work
I know how to do this when I'm explicitly defining the URL name in my urls.py file, but in this case, the URL is created as a Wagtail page and is handled by this line of my urls.py:
url(r"", include(wagtail_urls)),
I've been searching the documentation for whether Wagtail pages are assigned names that can be reversed to, but I'm finding nothing in the official documentation or here on StackOverflow.
The page object provides a get_url method - get_success_url should be able to accept this directly, so there's no need to use reverse.
def get_success_url(self):
return self.object.get_url()
Internally the URL route used by get_url is wagtail_serve, so if you absolutely need to go through reverse, you could replicate the logic there.

Sending form to another view django

I am building a website and I want various views that will ask the user to request a quote from our page. I want to keep the code as DRY as possible so I am writing a view quote which will receive the quote requests from various views and, if there is a validation error redirect back to the page that made the request. I managed to solve this using the super bad practice 'global variables'. I need a better solution, I would like redirecting to respective view with the current form so I can iterate through the form.errors. Here is my code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
global session_form
session_form = form
return redirect('Main:' + quote_for) # Here I would like to send form instead of storing in global variable`
You can use the HttpResponseRedirect function, and pass as argument the page that made the request.
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
All the META data is store on a dictionary, if you want to learn more check the documentation.
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
If you redirect to the referrer, form.errors will be empty, as redirection is always a GET request.
I can think of two solutions to your problem:
Submit forms asynchronously using JavaScript and so populate the errors
Make all the views containing the form support POST - one way to do this would be to create a base class that inherits from FormView
The second option is a typical way of handling forms in Django - you process both POST and GET inside the same view.
After two days of searching I finally found the answer. Instead of saving form in request.session I just save request.POST and then redirect. Here is the code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
request.session['invalid_form'] = request.POST
return redirect('Main:endview')
def endview(request):
session_form = request.session.pop('invalid_form', False)
if session_form:
form = Quote(session_form)
# render template again with invalid form ;)
Now I can repeat this with all the views I want and just change the what_for input of each form to match the respective view (Like I intended).

Providing parameters when reverse_lazy-ing a success_url redirect

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.

Dynamically alter urls.py to support dynamically created URLs?

I'd like to create a web service using Django that dynamically adds URLs to my urls.py file. Can this be done in Django? In other words, I'd like to have users be able to sign up for an end point that gets dynamically created using Django, e.g. my domain "dynamicurl.com" could add /johnp/ via a registration of user johnp. How would I do this?
Just create a pattern that matches the required characters for your username. Here is an example:
url(r'(?P<username>[\w.#+-]+)/$',
'yourapp.views.user_home', name='user-home'),
Then, when someone goes to yourdomain.com/johnp/ in your view you can do something like:
def user_home(request, username=None):
return render(request, 'user_home.html', {'username': username})
In user_home.html:
<strong>Welcome {{ username }}</strong>
Which will result in:
Welcome johnp
Consider this situation.
Suppose at some point in time you have a million users, your urls.py file will have a million records for user pages only. And I hope you do not wish to have separate views to handle all these separate urls.
Therefore, it is better define url patterns that can dynamically alter the content inside the templates depending on the value received within the url.
Using class based views, this can be done as follows:
In your urls.py file, write
url(r'^(?P<user_name>(.*))/$',ProfileView.as_view(),name='profile-view'),
class ProfileView(TemplateView):
template_name = "abc.html"
def get_context_data(self,**kwargs):
context = super(ProfileView,self).get_context_data(**kwargs)
context['user_name'] = kwargs['user_name']
return context
Then, in your template you can use it as {{user_name}}.

Django: Form in base template

Hi Stackoverflow people,
In my Django project I created a form to register users. This forms can be called through a specific url -> view method. Once the user has filled in the form, presses submit, the same view method will be called and if form.is_valid() is true, then ... simply a standard form, nothing special.
Now, I would like to integrate this little form on every page, and therefore I would like to add it to the base template. I have read that I could populate the form variable through a context_processor, but could I define the process after the submission of the form?
I have created the context_processor.py (as below), added the context_processor to the TEMPLATE_CONTEXT_PROCESSOR dir in the settings (as described here):
from app.forms import Form
def registration_form(request):
return {
registration_form : Form()
}
First of all, the form variable won't be displayed.
And secondly, how do I manipulate the form submission?
I think I misunderstanding the context_processor of Django and would be more than happy about comments on the overall process.
Thank you!
how are you trying to access to form in your template? you probably don't want to use a function name as your dictionary key, maybe you want
return {
'registration_form': Form(),
}
not sure what you mean by manipulate the form submission, but i guess you'd need all the form processing logic in your context processor
if request.POST:
form = Form(request.POST)
# validate etc
instead of creating context processor, create template tag for the purpose and place the tag in base.html
for form submission and displaying errors use ajax, and front-end validations.