Learning django & python.
Just set up a new site after doing the tutorial. Now for arguments sake say I want to add a bunch of About us, FAQ basic html pages with very limited dynamic elements do you go ahead and write a new line in my urls.py file for each page? or is their some neat way to say map all * *.html to the relevant .html file directly?
In general even if it does require a view will I have to write a new line in the url.py file for every page?
As long as there is some uniquely identifying section in the URL, you will not need to create an entry in urls.py for each direct-template url.
For example, you could say that all urls ending in ".html" are referencing a direct file from the templates.
urlpatterns = patterns('django.views.generic.simple',
(r'(.+\.html)$', 'direct_to_template'),
# ...
)
Take a look at http://docs.djangoproject.com/en/1.2/ref/generic-views/#django-views-generic-simple-direct-to-template for details.
Currently the best way to do this is using TemplateView from generic class-based views:
from django.views.generic.base import TemplateView
url(r'^$', TemplateView.as_view(template_name='index.html'), name='home'),
Write a url which grabs the static pages you're interested in
url(r'^(?P<page_name>about|faq|press|whatever)/$', 'myapp.staticpage', name='static-pages')
The staticpage view function in myapp
from django.views.generic.simple import direct_to_template
from django.http import Http404
def staticpage(request, page_name):
# Use some exception handling, just to be safe
try:
return direct_to_template(request, '%s.html' % (page_name, ))
except TemplateDoesNotExist:
raise Http404
Of course, you need to follow a naming convention for your templates, but this pattern can be expanded upon as needed.
This is better than the .+\.html pattern because it will treat templates which don't exist as 404s, whereas .+\.html will blow up with 500 errors if the template doesn't exist.
If you're using the class based views because direct_to_template has been deprecated, you can create a simple wrapper that renders your own templates directly:
from django.views.generic import TemplateView
from django.template import TemplateDoesNotExist
from django.http import Http404
class StaticView(TemplateView):
def get(self, request, page, *args, **kwargs):
self.template_name = page
response = super(StaticView, self).get(request, *args, **kwargs)
try:
return response.render()
except TemplateDoesNotExist:
raise Http404()
and in your router:
from myapp.static.views import StaticView
urlpatterns = patterns('',
url(r'^(?P<page>.+\.html)$', StaticView.as_view()),
# ...
)
One way to do this would be to write a single custom view that wraps the direct_to_template generic view. The wrapper could accept a parameter and accordingly form the name of the template and pass it to direct_to_template. This way you can route multiple pages with a single URL configuration.
Something like this:
url(r'^foo/(?P<page_name>\w+).html$', 'my_static_wrapper', name = 'my_static_wrapper'),
def my_static_wrapper(request, page_name):
# form template name and call direct_to_template
That said I suspect that there are better solutions out there though.
Slight Changes for latest versions of Django.
from django.views.generic.base import TemplateView
urlpatterns = [
path('',TemplateView.as_view(template_name='index.html'), name='home'),
]
Related
My views.py runs code fine when I press a button on my HTML page, views.py:
def start_or_end_fast(request):
#If starting fast, add a row to the db:
#fast_finished = False
#start_date_time using = current time
#end_date_time using = current time
if request.method == 'POST' and 'start_fast' in request.POST:
add_fast = logTimes(fast_finished=False,start_date_time=datetime.now(),end_date_time=datetime.now())
add_fast.save()
print(add_fast.start_date_time,add_fast.end_date_time)
print('Fast started')
#return render(request,'startandstoptimes/index.html')
return HttpResponseRedirect('startandstoptimes/index.html')
You can see my commented return line, this works but when I refresh the page I can resubmit the data, I want to avoid this. In researching my solution, I saw this could be solved using HttpResponseRedirect but I am not able to get this to work with my code, the more I change the more broken things become.
My application urls.py:
from turtle import home
from django.urls import path,include
from . import views
urlpatterns = [
path('', views.start_or_end_fast,name="start_or_end_fast")
]
My project urls.py:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('startandstoptimes.urls'))
]
I believe it is related to the URLs, due to the 404 message I see:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/startandstoptimes/index.html
Using the URLconf defined in myfastingsite.urls, Django tried these URL patterns, in this order:
admin/
[name='start_or_end_fast']
The current path, startandstoptimes/index.html, didn’t match any of these.
Am I going down the right route trying to use HttpResponseRedirect or is there a better solution?
class HttpResponseRedirect¶
The first argument to the constructor is required – the path to redirect to. This can be a fully qualified URL (e.g.
'https://www.yahoo.com/search/'), an absolute path with no domain
(e.g. '/search/'), or even a relative path (e.g. 'search/'). In that
last case, the client browser will reconstruct the full URL itself
according to the current path. See HttpResponse for other optional
constructor arguments. Note that this returns an HTTP status code 302.
See this link for more details: docs
As what the documentation says, HttpResponseRedirect accepts URL and not the path of your template. You should be doing it something like this:
from django.urls import reverse
return HttpResponseRedirect(reverse('start_or_end_fast'))
I'm using a django library called django-dashing that has a predefined set of urls that render a dashboard. I import them like this
urlpatterns = [
...
url(r'^dashboard/', include(dashing_router.urls)),
...
]
I want the router to be accessible only by administrators, which I can do with some config settings within django-dashing. However, when a non-admin user attempts to access /dashboard/, I want to redirect them to django's /admin/ panel to have them log in, instead of throwing the 403 that django-dashing does.
Since the django-dashing views are effectively blackboxed, I was wondering if there was a way to write a 'pre-view' that would intercept the request to /dashboard/, run some code – specifically, doing the appropriate redirects – and then continue onto the actual dashboard.
I know this would be easy enough to do by writing two urls, like /dashboard-auth/ which redirects to /dashboard/, but I don't want the user to have to go to one URL to get to another
Any suggestions?
A Django simple custom middleware is another option...
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
class DashingRedirectMiddleware(object):
def process_request(self, request):
if request.path.startswith('/dashing/') and not request.user.is_staff:
return HttpResponseRedirect(reverse('admin:login'))
return
Don't forget to add this middleware to your DJANGO SETTINGS...
MIDDLEWARE_CLASSES = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'yourapp.middleware.DashingRedirectMiddleware',
...
]
Or something like that.
The way I would do it is by overridding dashing's default router. All of the urls are generated dynamically by the Router class, so by overriding the get_urls method, you can wrap each function in the staff_member_required decorator.
from django.contrib.admin.views.decorators import staff_member_required
from django.conf.urls import url
from dashing.utils import Router
from dashing.views import Dashboard
class AdminRequiredRouter(Router):
def get_urls(self):
urlpatterns = [
url(r'^$', staff_member_required(Dashboard.as_view()), name='dashboard'),
]
for widget, basename, parameters in self.registry:
urlpatterns += [
url(r'/'.join((
r'^widgets/{}'.format(basename),
r'/'.join((r'(?P<{}>{})'.format(parameter, regex)
for parameter, regex in parameters.items())),
)),
staff_member_required(widget.as_view()),
name='widget_{}'.format(basename)),
]
return urlpatterns
router = AdminRequiredRouter()
Then include your router instead of dashing's
from .router import router
urlpatterns = [
...
url(r'^dashboard/', include(router.urls)),
...
]
If you are willing to look inside the 'black box' of the dashing urls, you can see that the /dashboard/ is handled by the Dashboard view. You could subclass this view, and catch the PermissionDenied error.
from django.core.exceptions import PermissionDenied
from dashing.views import Dashboard
class MyDashboard(Dashboard):
def get(self, request, *args, **kwargs):
try:
return super(MyDashboard, self).get(request, *args, **kwargs)
except PermissionDenied:
return redirect('/admin/')
Then add your view above the dashing urls:
urlpatterns = [
...
url(r'^dashboard/$', MyDashboard.as_view())
url(r'^dashboard/', include(dashing_router.urls)),
...
]
In the Django tutorial for the first app in Django we have
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
And then the urls.py has
from django.conf.urls import url
from polls import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
Now my question is what is the "request" parameter passed to the index function, also when the function index is called in urls.py it is not passed and variables it is just called as views.index in the line url(r'^$', views.index, name='index'),
The request parameter is a HttpRequest object, which contains data about the request (see the docs for django 3.2).
In your urls file, you are not calling the view.index function, just listing a reference to it. Django then calls the function when a matching request comes in and passes the HttpRequest object as a parameter.
This doesn't directly answer your question, but I suggest you watch this video:
A Scenic Drive through the Django Request-Response Cycle
This is a PyCon talk Dan Langer gave this year and showed how request and response work under the hood.
From Django Docs. Request came from User that want to load page.
When a page is requested, Django creates an HttpRequest object that
contains metadata about the request. Then Django loads the appropriate
view, passing the HttpRequest as the first argument to the view
function. Each view is responsible for returning an HttpResponse
object.
Building a blog in Django and I suspect something is wrong with matching my main urls.py
from django.conf import settings
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'$', 'posts.views.home'),
url(r'^(?P<slug>[\w-]+)/$', 'posts.views.single'),
)
Here's my views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response, RequestContext, Http404, get_object_or_404
from .models import Post
def home(request):
posts = Post.objects.filter(private=False)
return render_to_response('all.html', locals(), context_instance=RequestContext(request))
def single(request, slug):
post = Post.objects.filter(slug=slug)
return render_to_response('single.html', locals(), context_instance=RequestContext(request))
The function-based view home works perfectly and returns all non-private posts. However, the single view alters the URL to make the correct slug (ie: 127.0.0.1/this-correct-slug) but just goes to the top of the page and does nothing to filter the content (shows a 200 GET request in terminal). Using post = get_object_or_404(Post, slug=slug) yields same result.
I'm unsure of the post = Post.objects.filter(slug=slug) part but I also know it's not getting that far - trying to add print statements to see if the function is being called shows nothing.
I'm also a little unsure of the argument locals(). I've been using it but, frankly, only because I'm still not sure how to use the data dictionary.
Assume that the templates all.html and single.html are correct.
Thanks!
The problem is that your first regex is not rooted. It matches '$' which basically means "any string that ends" - which is everything. So all URLs end up being matched by that pattern.
It should be ^$, ie the empty string.
I am new to Django and just playing around with the code and features but I seem to be stuck (yes I have done the entire tutorial). For some reason my mysite/urls.py is not taking me to the right urls, actually it ONLY takes me to one template no matter what I do. Here is my urls.py:
from django.conf.urls.defaults import *
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('app.views',
(r"^app/$",'index'),
(r"^app/detail/$",'detail'),
url(r'^admin/', include(admin.site.urls)),
)
My app/views.py is:
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response
from django.http import HttpResponse
from mysite.app.models import Post,PostAdmin
def index(request):
return render_to_response("app/link1.html")
def detail(request):
return HttpResponse("You're looking at detail.")
I have a base.html template and link1 which {% extends base.html %}. But for some reason if I enter http://localhost:8000/app or http://localhost:8000/app/detail, I get the link1.html? Or even when I do http://localhost:8000/, I just get the link1.html. What could cause this? (Its probably something minor)
If I understand correctly shouldn't the url pattern with (r^"app/detail/$",'detail'), take me to the detail HttpResponse in views.py like the tutorial rather than a template?
Thank you
I have checked your code, it works just fine. I cannot replicate such error with your views and urls. Post your templates and directory structure to make sure they are fine too.
test result for /app/
Hello link 1.
test result for /app/detail/
You're looking at detail.