Django slug changes URL but doesn't get detail view - python

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.

Related

Avoid Django form being resubmitted by using HttpResponseRedirect

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'))

(Django) Trying to figure out how I can get the right product using query parameters on Postman (url.py and views.py)

I have been building a sandwich shop app and I succeesfully build models.py and inserted all the product data. However, when I try to call specific products using Postman and the Django server, it keeps showing 404. What I typed on postman is like so:
http://10.58.1.157:8000/product/sandwich?product_id=1
Below are my codes for urls.py and views.py
So far, I have tried:
urls.py
from django.urls import path
from .views import ProductView
urlpatterns = [
path('sandwich/int:<product_id>/', ProductView.as_view()),
]
and:
urls.py
path('sandwich/(?P<product_id>[\w.-]+)/', ProductView.as_view())
views.py
import json
import bcrypt
import jwt
from django.views import View
from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.db.models import Q
from .models import Product, Category, SubCategory
class ProductView(View):
def get(self, request):
product_id = request.GET.get('product_id', None)
return JsonResponse({'product_name':Product.objects.get(id=product_id).values()})
To clarify the GET request, I will add the screenshot of Postman below:
It seems like this is due to malformed URL path. That's whats typically indicative of 404-NotFound error.
You need to add question mark that essentially forms the querystring. It is processed and available as a dictionary-like object (a QueryDict) in request.GET in views.py
You can define it like so, with a ? using a REGEX pattern (You may also alter to your needs)
path('sandwich/(?P<product_id>[\w.-]+)/', ProductView.as_view()),
In your views.py you can filter them with
product_id = request.GET.get('product_id', None)
This should now hopefully return a response now that the URL cannot give a 404 error.
See this for an example
I finally solved the issue!
I looked at this page and improvised accordingly...
https://docs.djangoproject.com/en/3.0/topics/http/urls/#nested-arguments
the final version of request is:
http://127.0.0.1:8000/product/?product_id=1
and the urls.py is:
from django.urls import path, re_path
from .views import ProductView
urlpatterns = [
re_path(r'^(?:\?product_id=(?P<product_id>\d+)/)?$', ProductView.as_view()),
]

Understanding django.shortcuts.redirect

I have a couple problems understanding how redirect or rather reverse really work.
In the main urls.py I have:
from django.conf.urls import patterns, include, url
from django.views.generic.simple import redirect_to
urlpatterns = patterns('',
url(r'^$', redirect_to, {'url': '/monitor/'}),
url(r'^monitor/', include('monitor.urls')),
)
and in monitors.urls I have:
from django.conf.urls import patterns, include, url
urlpatterns = patterns('monitor.views',
(r'^$', 'index'),
(r'^abc/(?P<id>.*$)', 'abc'),
)
When you call /monitor I want to redirect it to /monitor/abc so I did:
def index(request):
return redirect("abc")
def abc(render, id=None):
return render_to_response("monitor/list.htmld", {})
But I got an NoReverseMatch exception. But when I do:
def index(request):
return redirect("abc/")
then it suddenly works.
I cannot fully understand why. Why did reverse fail with abc but not with abc/? And how does reverse know that the redirect should include monitor/ as well? What if I had in the main urls.py another app called xyz which also has a abc view?
Why did reverse fail with 'abc' but not with 'abc/'?
Because it interpreted it as a view name (and you indeed have a view named 'abc', see your monitor.urls file). This means Django will call reverse to compute the URL. The value abc/ is interpreted as an actual URL which means Django won't call reverse to determine the URL.
This also explains why reverse failed: the view with name abc also requires an argument called id. Otherwise Django won't be able to lookup the URL as there is no view called abc without parameters.
Based on the documentation you should be able to reverse the URL using:
redirect("abc", id=...)
where ... is the value of the id parameter.
And how does reverse know that the redirect should include monitor/ as well?
That is because it knows what URLs are available and 1) it knows where the view called abc is defined and 2) it knows that monitors.urls is included with monitor/ in front.
What if I had in the main urls.py another app called "xyz" which also has a "abc" view?
In that case you have to use namespaces.

Django urls.py template issue

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.

Django urls straight to html template

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'),
]

Categories