Create View - access to success_url from templates - django

say I have:
class MyCreate(CreateView):
template_name = 'template.html'
success_url = reverse_lazy('blabla')
form_class = MyForm
And suppose in my template I want to add a back button. The back will lead me to the same page as success_url. My solution was to override get_context_data in MyCreate class and add {'back': self.get_success_url()} to the context.
The implications is that I have more CreateViews and I had to create a ContextMixin for this back button. Is there any other easier solution? something like accessing success_url directly in my template?
Thanks

As we can see in django (1.7) implementation of ContextMixin, we must have access to view instance from our templates:
def get_context_data(self, **kwargs):
if 'view' not in kwargs:
kwargs['view'] = self
return kwargs
So you can access to success_url in templates:
{{ view.get_success_url }}

In my case using Django 1.10.3 the way to get it working was like this:
view:
from django.urls.base import reverse_lazy
class FooCreateView(CreateView):
success_url = reverse_lazy('name-of-some-view')
template:
{{ view.success_url }}
The try of using {{ view.get_success_url }} was leading to:
AttributeError 'NoneType' object has no attribute '__dict__'

Related

How to use get_queryset with Django DetailView with self.request.GET.get("q")

I am trying to figure out how to incorporate get_queryset with my Django detailview. In the normal use case, I have this working, add get_queryset to the DetailView and voila! It works....However this use case is a little different.
I am using a FormView to get a search value and then upon success, I return a detailview. This working properly too. When I try to incorporate a get_queryset to override the queryset, that's when things go awry.
Here's my code:
FormView
class AuthorSearchView(LoginRequiredMixin,FormView):
form_class = AuthorSearchForm
template_name = 'author_search.html'
def get_form_kwargs(self):
kwargs = super(AuthorSearchView, self).get_form_kwargs()
kwargs['user'] = self.request.user
kwargs['q'] = self.request.GET.get("q")
return kwargs
Then in my author_search.html:
<form method="GET" autocomplete=off action="{% url 'Author:author_search_detail' %}">
When the user enters a value in the search...it returns a DetailView screen:
class AuthorSearchDetailView(LoginRequiredMixin,DetailView):
model = Author
context_object_name = 'author_detail'
template_name = 'author_search_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(Author, request_number=self.request.GET.get("q"))
return get_object_or_404
The code above works fine. Note I am not using a PK reference in my action reference, as it is not needed for this approach. My URL in the case of the code above is:
url(r'^author_search_detail/$',views.AuthorSearchDetailView.as_view(), name='author_search_detail'),
However, when I try to incorporate get_queryset instead of get_object with the code below:
def get_queryset(self):
queryset = super(AuthorSearchDetailView, self).get_queryset()
return queryset.filter(request_number=self.request.GET.get("q"))
Then I get:
AuthorSearchDetailView must be called with either an object pk or a slug in the URLconf.
I get this is because I'm using DetailView and not providing a PK in my URL.
However, when I go to add a pk in my HTML and my URL as shown below:
<form method="GET" autocomplete=off action="{% url 'Author:author_search_detail'pk=author.pk %}">
URL:
url(r'^author_search_detail/(?P<pk>\d+)/$',views.AuthorSearchDetailView.as_view(), name='author_search_detail'),
I get....
Reverse for 'author_search_detail' with keyword arguments '{'pk': ''}' not found. 1 pattern(s) tried: ['Author/author_search_detail/(?P<pk>\\d+)/$']
Curiously, if I substitute pk=author.pk with pk=user.pk, I don't get the error. So this leads me to believe that because I'm using FormView initially to get the DetailView success_url...that there is a problem with the pk reference in the FormView. It doesn't know about the PK.
So after too much thinking about this one...turns out I was overthinking it...
I just needed to update my get_object to the filtered criteria...
def get_object(self, queryset=None):
return get_object_or_404(Author, request_number=self.request.GET.get("q"),user=self.request.user,id="1")
return get_object_or_404
Added user and ID to the filter criteria of get_object.

Issue with Django URL Mapping/DetailView

I am new to Django and have been making a sample project. I have been trying to use Generic Detailview. It seems that url redirection works fine but DetailView can't get primarykey from the url.
Main url.py::
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^',include('personal.urls')),
]
Here is my app's urls.py code:
urlpatterns = [
url(r'(?P<pk>\d+)/$',views.detailView.as_view(),name="detail"),]
View file for the DetailView:
from django.shortcuts import render
from django.views import generic
from .models import Story
class detailView(generic.DetailView):
model = Story
template_name = 'personal/storydetail.html'
def get_context_data(self, **kwargs):
pk = kwargs.get('pk') # this is the primary key from your URL
print("PK:",pk)
Template Code:
{% block content %}
{{ Story.writer }}
<h6> on {{ Story.story_title }}</h6>
<div class = "container">
{{ Story.collection }}
</div>
{% endblock %}
Story Class code:
class Story(models.Model):
story_title = models.CharField(max_length=200) #Story title
writer = models.CharField(max_length=200) #WriterName
collection=models.CharField(max_length=200) #Collection/Book name
When I check primary key value on view it shows it 'NONE'. I can't find issue with the code. My pased url looks like : http://127.0.0.1:8000/personal/2/ where personal is the name of app and 2 should be taken as id.
The problem is that you are using kwargs instead of self.kwargs inside the get_context_data method. It should be something like:
def get_context_data(self, **kwargs):
# You need to call super() here, so that the context from the DetailView is included
kwargs = super(detailView, self).get_context_data(**kwargs)
pk = self.kwargs['pk'] # No need for get() here -- if you get a KeyError then you have a problem in your URL config that should be fixe # this is the primary key from your URL
# edit kwargs as necessary
...
return kwargs
In the get_context_data method, kwargs are those passed to the method to make up the context. They are different from self.kwargs, which are from the url pattern.

Django: Generic View not working

i am following the django tutorial on the web site and i am stuck on using the generic view.
views.py
class DetailView(generic.DetailView):
model = Questions
template_name = 'tolls/detail.html'
urls.py
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail')
detail.html
<h1> {{question.question_text}} </h1>
Nothing is displayed in my details.html
If i don't use generic view it works with below function and url
view.py
def detial(request, question_id):
question = get_object_or_404(Questions, pk=question_id)
return render(request, 'tolls/detail.html', {'question': question})
urls.py
`url(r'^(?P<question_id>[0-9]+)/$', views.detial, name='detail'),`
The default name for accessing your model instance is object. So either use {{ object.question_text }} in your template or specify a name in your view class using context_object_name:
class DetailView(generic.DetailView):
model = Questions
template_name = 'tolls/detail.html'
context_object_name = 'question'

Passing context value to profile signup template in django-allauth

With django-allauth, I am forcing a new user to fill out additional profile information on signup using a custom ACCOUNT_SIGNUP_FORM.
settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'profiles.signup.ProfileSignupForm'
SOCIALACCOUNT_AUTO_SIGNUP = False
This ProfileSignupForm is then rendered in a modified allauth/templates/socialaccount/signup.html template. This modified template renders the logo of the new user's company that is defined in the new user's session (I used an invitation link that first goes to a RedirectView, writes into the session, and then forwards to the new signup).
signup.html
<html>
<body>
<img src="{{ logo }}" />
{% crispy form %}
</body>
</html>
How can I pull the company logo from my session and pass it to my template without forking the repository and modifying the SignUp View?
That approach would look like this:
class SignupView(RedirectAuthenticatedUserMixin, CloseableSignupMixin, FormView):
def dispatch(self, request, *args, **kwargs):
...
self.company = request.session.get('company')
...
def get_context_data(self, **kwargs):
...
context['logo'] = company.logo
...
Either you can follow the above mentioned way to Inherit the View and Define custom url for Signup to use your view.
Or
you car directly access company logo in your template as:
{{ request.session.company.logo }}
This can be done because request is available as a context variable in Templates if rendered with RequestContext instance.
you can inherit SignupView directly in your code instead of forking and modifying the original SignupView.
class MySignupView(SignupView):
def dispatch(self, request, *args, **kwargs):
...
self.company = request.session.get('company')
...
return super(MySignupView, self).dispatch(request, *args, *kwargs)
def get_context_data(self, **kwargs):
context = super(MySignupView, self).get_context_data(**kwargs)
context['logo'] = self.company.logo
return context
Then using MysignupView.as_view() in the urls.py.

Access named URL parameter in Template or Middleware

In my url conf, I have several URL's which have the same named parameter, user_id.
Is it possible to access this parameter either in a middleware - so I can generically pass it on to the context_data - or in the template itself?
Sample URL conf to illustrate the question:
url(r'^b/(?P<user_id>[0-9]+)/edit?$', user.edit.EditUser.as_view(), name='user_edit'),
url(r'^b/(?P<user_id>[0-9]+)/delete?$', user.delete.DeleteUser.as_view(), name='user_delete')
For class based views, the view is already available in the context, so you dont need to do anything on the view side. In the template, just do the following:
{{ view.kwargs.user_id }}
See this answer
If you need this data in the template, just override your view's get_context_data method:
class MyView(View):
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['user_id'] = self.kwargs.get('user_id')
return context
For function based views:
template
{% url 'view' PARAM=request.resolver_match.kwargs.PARAM %}
views.py
def myview(request, PARAM):
...
Django 2.2