Django: Class-based views, URL and template_name - python

I am trying to use something like polls:detailin a class-based view, such as this:
class QuestionDetail(DetailView):
template_name = 'polls:result'
However, I get a TemplateDoesNotExist at /polls/2/result
polls:result error...
The urls.py is:
from django.conf.urls import url
from polls.views import IndexView, DetailView, ResultsView
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', DetailView.as_view(), name='detail'),
url(r'^(?P<pk>\d+)/result$', ResultsView.as_view(), name='result'),
url(r'^(?P<pk>\d+)/vote$', views.vote, name='vote'),
]
I guess the main question is, how do I use the names of URLs in class-based views, instead of explicitly providing the template name, such as polls/question_results.html?
Is there anything other than template_name?
I was reading that it's a good practice to use names in URLS so in case the URL itself changes, the rest of the code still works, so that's what I'm trying to do.

Url name and template name are very absolutely things.
template_name is a path to the .html file.
Url's name parameter you can use to reverse url from name using django.urls.reverse

You can use the name of URL like this:
django.urls.reverse('polls:detail', args=[object.id])
And you have to change the template_name settings as well and to create a template detail.html in your current template folder.
template_name = 'polls/detail.html'

Related

Django,DRF, get another app's view name for a reverse

I'm trying to create a link for another app in my serializer using the solution provided here:
https://stackoverflow.com/a/45850334/12177026
I'm trying to match the view's name but every way I try I get this error:
Reverse for 'KnownLocationView' not found. 'KnownLocationView' is not a valid view function or pattern name.
serializers:
class MissionSerializer(HyperlinkedModelSerializer):
gdt = ChoiceField(choices=lazy(get_locations, tuple)())
location = SerializerMethodField(label='Open Location')
def get_location(self, obj):
request = self.context.get('request')
return request.build_absolute_uri(reverse('KnownLocationView', kwargs={'Name': obj.gdt}))
class Meta:
model = Mission
fields = ('MissionName', 'UavLatitude', 'UavLongitude', 'UavElevation', 'Area',
'gdt', 'location')
KnownLoction/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import KnownLocationView
app_name = 'KnownLocation'
router = DefaultRouter()
router.register(r'knownlocations', KnownLocationView)
urlpatterns = [
path('', include(router.urls)),
]
I tried replacing view_name with either of the following:
'knownlocations'
'KnownLocation:knownlocations'
'KnownLocation:KnownLocationView'
But get the same error
even tried to reorder the installed apps.
api/urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', include('landingpage.urls')), # API Landing Page
path('', include('ThreeLocations.urls')), # Triangulation between two Known GDTs and uav location.
path('', include('SecondGDT.urls')), # Options For Second GDT Positioning.
path('', include('KnownLocation.urls', namespace='knownlocations')),
# Add Known Location To the map.
] + staticfiles_urlpatterns()
KnownLocation/views.py
from rest_framework.renderers import AdminRenderer
from rest_framework.viewsets import ModelViewSet
from .models import KnownLocation
from .serializers import KnownLocationSerializer
class KnownLocationView(ModelViewSet):
"""
List of Known Locations In which we can place one of our ground positions.
press create to add a new location.
"""
serializer_class = KnownLocationSerializer
queryset = KnownLocation.objects.all()
def get_serializer_context(self):
data = KnownLocationSerializer(self.queryset, many=True, context={'request': self.request})
return data
renderer_classes = [AdminRenderer]
I think you should try to change how you are calling the reverse.
You can find reference to it below
Note: If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters on the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.
The automatic view_name generation uses a pattern like %(model_name)-detail. Unless your models names actually clash you may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.
Try using this
reverse('KnownLocationView:mission-detail, kwargs={'Name': obj.gdt})
Or
reverse('mission-detail, kwargs={'Name': obj.gdt})

'TemplateDoesNotExist': template loader appears to be searching non-existent routes

I'm receiving a TemplateDoesNotExist error when I click on a particular link, and I've noticed from the 'template-loader postmortem' that Django is searching incorrect and non-existent paths.
I've recently moved half of the contents of a Django app called 'reviews' into another called 'accelerators'. My templates directory for each app follows the pattern: '"app name"/templates(folder)/"app name"/html templates'.
Having moved the template into the accelerators app (and having updated my settings and urls), Django should be looking for the template via 'accelerators/templates/accelerators/accelerator_form.html', but according to the error message it's instead searching: 'accelerators/templates/reviews/accelerator_form.html'.
I suspect this has something to do with the fact that I've just moved this template, alongside a number of other files, from the reviews app, but I can't figure out why this is happening. I've included my updated urls etc. below for reference.
Base directory urlpatterns
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(template_name='accounts/logout.html'), name='logout'),
path('', include('accounts.urls')),
path('reviews/', include('reviews.urls')),
path('accelerators/', include('accelerators.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
accelerators/urls.py
from django.urls import path
from .views import (
AcceleratorListView,
accelerator_detail,
accelerator_reviews,
AcceleratorCreateView,
AcceleratorUpdateView,
AcceleratorDeleteView,
)
from . import views
urlpatterns = [
path('', AcceleratorListView.as_view(), name='accelerators'),
path('<int:pk>/', views.accelerator_detail, name='accelerator_detail'),
path('new/', AcceleratorCreateView.as_view(), name='accelerator_create'),
path('<int:pk>/update/', AcceleratorUpdateView.as_view(), name='accelerator_update'),
path('<int:pk>/delete/', AcceleratorDeleteView.as_view(), name='accelerator_delete'),
path('<int:pk>/reviews/', views.accelerator_reviews, name='accelerator_reviews'),
]
accelerators/views.py (view that I'm trying to access)
class AcceleratorCreateView(LoginRequiredMixin, CreateView):
model = Accelerator
fields = ['name', 'website', 'locations', 'bio', 'sector_focus', 'stage', 'deal', 'duration', 'logo']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
base.html template (relevant link)
<li><a class="dropdown-link" href="{% url 'accelerator_create' %}">Add Company</a></li>
That's happening because your Accelerator model is still coming from review app i.e. from review.models.
Django searches for template in templates/app_name/model_name_type.html if it not provided specifically.
2 Solutions: Follow any of them
Provide template_name specifically.
class AcceleratorCreateView(LoginRequiredMixin, CreateView):
model = Accelerator
template_name = 'accelerators/accelerator_form.html'
Move your Accelerator model to models file of accelerators app.

LoginView with Success

I am currently working with my Django authentification app. My goal is once the user logged in successful I want to redirect it to the index page where my code shows a customised message:
messages.success(request, 'Login Successful', extra_tags='alert alert-dark')
My problem is I didn't manage to 'access' LoginView in my views.py.
I read about SuccessMessageMixin, but this LoginView won't work (Template Not Found):
class LoginView(auth_views.LoginView):
template_name = "accounts/login.html"
Do you have any idea how to solve this?
Only as long I include template_name in my urls.py it works, but I can't add the success message mixin there.
urls.py
from django.urls import path
from django.contrib.auth import views as auth_views
from . import views
app_name = 'accounts'
urlpatterns = [
path('login/', auth_views.LoginView.as_view(template_name='accounts/login.html'), name='login'),
]
You have a custom LoginView, but in the urls.py you call auth_views.LoginView.
To call your custom view you should do
from <module_name>.views import LoginView
urlpatterns = [
path('login/', LoginView.as_view(), name='login'),
]
It's a good idea to have a different name for your custom view, i.e.CustomLoginView.
You can add the followng snippet in the settings.py files:
LOGIN_REDIRECT_URL = '/'
You can add your own index page URL in 'LOGIN_REDIRECT_URL' variable

password_change_done not using custom template in Django

I used Django's auth_views.password_change to allow a user to change their password. It uses a custom template and it works well, but the problem I'm having is that it redirects to Django's password change successful admin template instead of the custom template I declared.
urls.py
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from . import views
app_name = 'users'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^add-user/$', views.CreateUserView.as_view(), name='add-user'),
url(r'^search/$', views.UserSearchView.as_view(), name='search-users'),
url(r'^login/$', views.LoginView.as_view(), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
url(r'^(?P<pk>\d+)/settings/update$', views.UpdateAccountView.as_view(), name='update-account'),
url(r'^settings/change/$', auth_views.password_change, {'template_name': 'users/forms/change-password.html'},
name='change-password'),
url(r'^settings/change-done/$', auth_views.password_change_done,
{'template_name': 'users/forms/change-password-done.html'}, name='change-password-done'),
url(r'^(?P<pk>\d+)/delete-user/$', views.DeleteUserView.as_view(), name='delete-user'),
Any Ideas what I missed?
In INSTALLED_APPS ensure that your app with the custom templates is specified before admin.
Django searches for the template in order.
You should use:
url(r'^settings/change/$', auth_views.password_change, {'template_name': 'users/forms/change-password.html',post_change_redirect:'change-password-done'},name='change-password'),
Without post_change_redirect default template will be loaded in that case.
Quite long since this question was asked, but the above answers didn't work for me and this did. In urls.py:
Make sure this import is there:
from django.contrib.auth.views import ..., password_change_done, ...
Then remove url(r'^', include('django.contrib.auth.urls')) from the URLs and override the password_change_done view with your custom template. Again, I also had the above suggestions already implemented in my code.

Django views and URl's

I'm having a problem with the way my URL's look in Django. I have a view like this:
def updatetext(request, fb_id):
Account.objects.filter(id=fb_id).update(display_hashtag=request.POST['hashtag'])
fb = get_object_or_404(Account, pk=fb_id)
return render(request, 'myapp/account.html', {
'success_message': "Success: Settings updated.",
'user': fb
})
When a user clicks on the URL to update the text they are then redirected to the account page but the URL then looks like 'account/updatetext/'. I would like it just be 'account/'.
How would I do this in Django. What would I use in place of render that would still allow me to pass request, 'success_message' and 'user' into the returned page but to not contain the 'updatetext' within the URL?
[edit]
The urls.py file looks like this:
from django.conf.urls import patterns, url
from myapp import views
urlpatterns = patterns('',
url(r'^home/$', views.index, name='index'),
url(r'^(?P<fb_id>\d+)/$', views.account, name='account'),
url(r'^(?P<fb_id>\d+)/updatetext/$', views.updatetext, name='updatetext'),
url(r'^(?P<fb_id>\d+)/updatepages/$', views.updatepages, name='updatepages'),
url(r'^login/$', views.user_login, name='login'),
url(r'^logout/$', views.user_logout, name='logout'),
url(r'^admin/$', views.useradmin, name='admin'),
)
You need to actually redirect the user to '/account/'. Rather than returning a call to render you can do the following:
from django.http import HttpResponseRedirect
def updatetext(request, fb_id):
Account.objects.filter(id=fb_id).update(display_hashtag=request.POST['hashtag'])
fb = get_object_or_404(Account, pk=fb_id)
return HttpResponseRedirect(reverse('account', kwargs={"fb_id": fb_id}))
However, it would be better to pass in a call to reverse into the HttpResponseRedirect constructor, but since I don't know your urls.py I just wrote the relative url.

Categories