I have a good many class based views that use reverse(name, args) to find urls and pass this to templates. However, the problem is class based views must be instantiated before urlpatterns can be defined. This means the class is instantiated while urlpatterns is empty leading to reverse throwing errors. I've been working around this by passing lambda: reverse(name, args) to my templates but surely there is a better solution.
As a simple example the following fails with exception:
ImproperlyConfigured at xxxx
The included urlconf mysite.urls doesn't have any patterns in it
mysite.urls
from mysite.views import MyClassView
urlpatterns = patterns('',
url(r'^$' MyClassView.as_view(), name='home')
)
views.py
class MyClassView(View):
def get(self, request):
home_url = reverse('home')
return render(request, 'home.html', {'home_url':home_url})
home.html
<p><a href={{ home_url }}>Home</a></p>
I'm currently working around the problem by forcing reverse to run on template rendering by changing views.py to
class MyClassView(View):
def get(self, request):
home_url = lambda: reverse('home')
return render(request, 'home.html', {'home_url':home_url})
and it works, but this is really ugly and surely there is a better way. So is there a way to use reverse in class based views but avoid the cyclic dependency of urlpatterns requiring view requiring reverse requiring urlpatterns...
EDIT:
I'm using this as so:
views.py
def sidebar_activeusers(cls):
sidebar_dict = {'items' = []}
qs = models.random.filter.on.users
for user in qs:
item = {
'value': user.name,
'href': reverse('user_profile', args=[hash_id(user.id)])}
sidebar = loader.get_template('sidebar.html')
cls.base_dict['sidebar_html'] = sidebar.render(Context(sidebar_dict))
return cls
#sidebar_activeusers
class MyView1(View):
base_dict = {}
...
#other_sidebar_that_uses_same_sidebar_template
class MyView2(View):
basically I'd like to use the same sidebar template for a few different content types. Although the models displayed in the sidebar will be arbitrary the format will always be the same.
For your example MyClassView, it's ok to use reverse
class MyClassView(View):
def get(self, request):
home_url = reverse_lazy('home')
return render(request, 'home.html', {'home_url': home_url},
The reverse method is not called when the class is defined -- It is only called when the request is processed and the get method is called, so there shouldn't be an error. I have tested the example above and it works fine.
However, When you use your sidebar_activeusers decorator, the reverse call occurs before the url conf is loaded. In cases like these, you can use reverse_lazy. Here's an example of reverse_lazy in action:
from django.core.urlresolvers import reverse_lazy
class MyOtherClassView(View):
home_url = reverse_lazy('home')
def get(self, request):
return render(request, 'home.html', {'home_url':self.home_url})
This time, home_url is set when the class is defined, so reverse_lazy is required instead of reverse, because the url conf hasn't loaded yet.
Your home url can be accessed directly in your template by using declaring {% url 'home' %}. It's a standard way to access your named urls in your urls.py file.
You do not have to send the home_url variable to your template explicitly in your class-based view function.
In other words, in your home.html file:-
<p>Home</p>
will do.
Related
Hello Community!
I am creating a small blog with Django, in which I have a single application. It happens that I have already defined a large part of the blog, this is:
The Home view.
Views for the categories of each publication.
View for each of the posts
Among other
Now that I have wanted to add the "About Author" view, when it should redirect to its respective HTML template, Django ends up redirecting itself to another template, which generates a NoReverseMatch error.
Simplifying the code, this is:
views.py:
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post, Author, Category
class Home(ListView):
def get(self, request, *args, **kwargs):
context = {
'post': Post.objects.get(title='NamePost')
}
return render(request, 'PageWebApp/home.html', context)
class PostSimple(DetailView):
def get(self, request, slug_post, *args, **kwargs)
context = {
'post': Post.objects.filter(slug_post=slug_post)
}
return render(request, 'PageWebApp/page-simple.html', context)
class PostsCategory(DetailView):
def get(self, request, category, *args, **kwargs):
# View that shows each of the categories within the blog
context = {
'categories': Category.objects.get(category=category)
}
return render(request, 'PageWebApp/posts-category.html', context)
class AboutAuthor(DetailView):
def get(self, request, slug_autor, *args, **kwargs):
context = {
'author': Author.objects.get(slug_author=slug_author)
}
return render(request, 'PageWebApp/page-author.html', context)
urls.py
from django.contrib import admin
from django.urls import path
from PageWebApp import views
urlpatterns = [
path ('', views.Home.as_view (), name = 'home'),
# [Here are the URLs to the other project templates (they work fine)]
# Next the conflictive ones:
path ('posts- <category> /', views.PostsCategory.as_view (), name = 'posts-category'),
path ('<slug: slug_post> /', views.PostSimple.as_view (), name = 'page-simple'),
path ('about-<slug: slug_author> /', views.AboutAuthor.as_view (), name = 'page-author'),
]
I have a template called "base.html" that all the others inherit.
In the Start template, called "home.html" we can achieve, among other things, the following:
<! - HERE GO OTHER TAGS THAT REDIRECT TO OTHER URLS ->
<h4> See posts from {{categories.category}} </h4>
<h4> <a href="{% url 'page-simple' post.slug_post %}> {{post.title}} </a> </h4>
<h4> <a href="{% url 'page-author' author.slug_author %}> By: {{author.name}} </a> </h4>
As I mentioned earlier, when entering the home window "home.html" I have a series of "a" tags that redirect to various templates, but specifically when I select to go to the URL of the page-author.html template, for some reason Django interprets that it should redirect to page-category and it gives me the error described.
Reverse for 'posts-category' with arguments '('',)' not found. 1 pattern(s) tried: ['posts\\-(?P<category>[^/]+)/$']
I have thoroughly reviewed each HTML template and they are all correctly redirected to the corresponding URLs.
Thanks in advance for your responses and comments.
First of all is it right that there are spaces in your paths? I don't know if that can break anything.
Anyways I think it has to do with the way your paths are set up. This path is catching every path path ('<slug: slug_post> /'. This way all the paths underneath it are first caught by that path because they match that pattern. What you can do in your example is change the order like this:
path ('posts- <category> /', views.PostsCategory.as_view (), name = 'posts-category'),
path ('about-<slug: slug_author> /', views.AboutAuthor.as_view (), name = 'page-author'),
path ('<slug: slug_post> /', views.PostSimple.as_view (), name = 'page-simple'),
This will probably work but the cleanest way to do it is to differentiate the paths based on a / like:
path ('posts/<category>/category/', views.PostsCategory.as_view (), name = 'posts-category'),
path ('posts/<slug: slug_post>/', views.PostSimple.as_view (), name = 'page-simple'),
path ('authors/<slug: slug_author>/', views.AboutAuthor.as_view (), name = 'page-author'),
I'm having a problem with the views for my blog main page. I've read a lot of problems with the same error but couldn't find a solution that i thought matched with my problem.
I am trying to emulate my old code with a new View so that i can customise it further in the future.
path('', ListView.as_view(
queryset=Post.objects.filter(posted_date__lte=now).order_by("-posted_date")[:25],
template_name="blog/blog.html")),
i was previously using just the above url pattern to display my blog posts but im moving to a view:
def BlogViews(TemplateView):
def get(self, request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
and the new url
path('', BlogViews, name='blog'),
However this view is not working and returns the error in the title.
I'm importing TemplateView, Post and render correctly i believe.
TemplateView is a class-based-view, so you subclass it with class.
class BlogViews(TemplateView):
def get(self, request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
Then use .as_view() in the URL pattern.
path('', BlogViews.as_view(), name='blog'),
Avoid overriding get or post for class-based views. You end up losing the functionality of the parent class or duplicating it. You can take your ListView.as_view(...) as the starting point for the view, for example:
class BlogView(ListView):
template_name="blog/blog.html"
def get_queryset(self):
# Use get_queryset instead of queryset because you want timezone.now()
# to be called when the view runs, not when the server starts
return Post.objects.filter(posted_date__lte=timezone.now()).order_by("-posted_date")[:25]
In your case, it might be easier to use a function-based-view:
def blog_view(request):
posts = Post.objects.all().order_by("-posted_date")[:25]
args = {
'posts' : posts,
}
return render(request, 'blog/blog.html', args)
Then include it in the URLs with:
path('', blog_view, name='blog'),
In the last few weeks I've switched from developing an application that processes a simple XML file, then writes its contents to an Oracle DB (cx_Oracle) and outputs in HTML, to using a Django framework. The Django framework switch wasn't necessary, but since I have an opportunity to develop something by using Django, I thought why not as it is a new area for me and can't damage my CV.
Anyway, I'm having issues with knowing what to write in my urls.py file when importing a Class from the views.py file. Here are the current contents:
urls.py
from myproj.views import Pymat_program
pymatProgram = Pymat_program()
urlpatterns = (
url(r'^pymat/$', pymatProgram.abc),
)
views.py
class Pymat_program:
def abc(self, request):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
I've tried various permutations of using request, not using request, and also how the class is called in the url tuple, all to no avail. When I use a definition outside of the class (i.e. not in any class), then this is correctly displayed in HTML.
You don't want to wrap your program in a class. (In general, in Python you should treat modules like, say, Java might treat classes with static members only.)
There are two approaches, really:
Function-based views
urls.py
from myproj.views import abc_view
urlpatterns = (
url(r'^pymat/$', abc_view),
)
views.py
def abc_view(request):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
Class-based views
urls.py
from myproj.views import AbcView
urlpatterns = (
url(r'^pymat/$', AbcView.as_view()),
)
views.py
from django.views.generic import View
class AbcView(View):
def get(self, request, *args, **kwargs):
test = "<html><body>Random text.</body></html>"
return HttpResponse(test)
Since you are using class based views in Django, so you have to call this class in URL like:
from myproj.views import Pymat_program
pymatProgram = Pymat_program()
urlpatterns = (
url(r'^pymat/$', pymatProgram.as_view()),
)
and then use get or post methods name in class like:
class Pymat_program:
model = ModelName
def post(self, request, *args, **kwargs):
....
2nd way is to use function based views:
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^articles/2003/$', views.view_name),
]
views.py
from django.http import HttpResponse
import datetime
def view_name(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
for more details look into class based views docs
Error log:
error HomePageView' object has no attribute 'META'
urls.py
from django.conf.urls import url
from hello.views import HomePageView
urlpatterns = (
url(r'^$', HomePageView.as_view(), name='home'),
)
views.py
import textwrap
from django.shortcuts import render
from django.utils import timezone
from django.http import HttpResponse
from django.views.generic.base import View
class HomePageView(View):
def dispatch(request, *args, **kwargs):
c = {}
return render(request, 'welcome.html', c)
welcome.html
<html>
<head>
<title>Greetings to Django</title>
</head>
<body>
<h1 style="color:green;" align="center">Greetings to the world of Django Web Framework </h1>
</body>
</html>
The first parameter to dispatch, like all instance methods, should be self. request is passed as a keyword argument.
Note however that you should not be overriding dispatch. By doing so you are negating all the benefits of using class based views; you might as well use a standalone function. Instead, subclass TemplateView, set template_name as a class attribute, and define get_context_data when you actually need to pass some data to the template.
As already you are called view by name then It would be much better you can simply import all views like :
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$',HomePageView.as_view(), name='home'),
]
and mostly we use camelcase to define view name as: homePageView()
please go through it. https://docs.djangoproject.com/en/1.10/intro/tutorial01/#url-argument-name
In your views.py file, in a class function you need to use self as an argument.
Change this line def dispatch(request, *args, **kwargs): to def dispatch(self, request, *args, **kwargs):
class HomePageView(View):
def dispatch(self, request, *args, **kwargs):
c = {}
return render(request, 'welcome.html', c)
I have a class based view which just displays a list of configurations.
This view is added to the Django Admin site by using the following code:
#admin.register(ZbxHostConf)
class ZbxHostConfListViewAdmin(admin.ModelAdmin):
review_template = 'admin/admzbxhostconf_list.html'
def get_urls(self):
urls = super(ZbxHostConfListViewAdmin, self).get_urls()
my_urls = patterns('',
(r'^zbxhostconflist/$', self.admin_site.admin_view(self.review)),
)
return my_urls + urls
def review(self, request):
return ZbxHostConfListView.as_view()(request)
The template extends the admin/base_site.html template. I can access the site only after I log in to the Django Admin site. Unfortunately the template cannot access the context data provided by the admin view.
As the Django documentation suggests the context data will be provided directly to the TemplateResponse function:
def my_view(self, request):
# ...
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key=value,
)
return TemplateResponse(request, "sometemplate.html", context)
For function-based views there is the possibility of the extra_context argument but class-based views don't provide this argument. I guess I have to modify the get_context_data function but I don't really understand how I can provide the admin context data to the get_context_data function of my class based view. Any suggestions?
This might not be a correct answer, but I believed you could try something like this.
#!/usr/bin/python3
from django.contrib import admin
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update(admin.site.each_context(self.request))
return context