I am making a blog and trying to make pagination operations on post listing page. When I run my app, my URL contains unexpected characters. For example ;
http://127.0.0.1:8000/blog/%5E$
I couldn't understand why %5E$ is there.
Here my urls.py (this is in blogapp):
from django.urls import path,include
from django.contrib import admin
from . import views
urlpatterns = [
path(r'^$',views.getPosts,name="bloghome"),
path(r'^(?P<selected_page>\d+)/?$',views.getPosts,name="bloghome"),
path('<slug>',views.postDetailPage,name="post_detail")
]
getPost function in views.py
def getPosts(request,selected_page=1):
# latest_post = Posts.objects.get(id=1)
posts = Posts.objects.all().order_by('-pub_date')
pages = Paginator(posts,5) #Show 5 post per page
try:
returned_page = pages.page(selected_page)
except EmptyPage:
returned_page = pages.page(pages.num_pages)
#content = pages.page(selected_page)
return render(request,'blog.html',{'page':returned_page,
'posts':returned_page.object_list
})
And finally, this bloglist page is being entered from homepage with <a> tag. Here it's one line of code:
Blog
Based upon https://docs.djangoproject.com/en/dev/ref/urls/#django.urls.path, you need to use re_path() instead of path(), as it is interpreting ^$ literally, per zvadym's prior comment. This is new in Django 2.0, so it depends on your version.
Related
I'm trying to use an escaped url as a re_path variable for an object identifier in my API. The logic to connect the escaped url to an object is there, but I can't figure out why the regex is not matching.
In my head, a GET request with the following url /objects/http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1/foo should be parsed into obj = 'http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1' and field = 'foo' for further processing. Ultimately, returning the object and 200. However I am getting a 404 with a very specific Django error that only proliferates when Django unfruitfully iterates through all the paths available.
<HttpResponseNotFound status_code=404, "text/html">
(Pdb) response.content
b'\n<!doctype html>\n<html lang="en">\n<head>\n <title>Not Found</title>\n</head>\n<body>\n <h1>Not Found</h1><p>The requested resource was not found on this server.</p>\n</body>\n</html>\n'
I know the path exists as when I examine the urlpatterns, the path is present:
(Pdb) pp object_router.get_urls()
[
...
<URLPattern '^(?P<obj>https?[-a-zA-Z0-9%._\+~#=]+)/(?P<field>foo|bar)\/?$' [name='test-detail-foobar']>
]
The url is escaped with urllib.parse.quote(obj.url, safe="")
Regexs tried:
r"https?[-a-zA-Z0-9%._+~#=]+"
r"https?[%23A](www\.)?[-a-zA-Z0-9#:%._\+~#=]{2,256}(\.[a-z]{2,6})?\b([-a-zA-Z0-9#:%_\+.~#?&//=]*)(?=\/foo)" https://regexr.com/6ue7b
r"(https?://(www.)?)?[-a-zA-Z0-9#:%.+~#=]{2,256}(.[a-z]{2,6})?\b([-a-zA-Z0-9#:%+.~#?&//=]*)
Edit:
Based off the Django Path Convertor path regex, I've changed my regex to https?.+ with the compiled version as '(?P<obj>https?.+)/(?P<field>foo|bar)\\/?$'. This is moving in the right direction, however I've further identified some weirdness. Basically it seems that escaping the path variable url (obj) is partially to blame for the mismatch as an unescaped url (without query parameters) will return a differently handled API response. Further more, adding a query parameters/a question mark, once again returns us back to the Django 404.
Consider a simple project like this:
urls.py
from django.contrib import admin
from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r"https?[-a-zA-Z0-9%._+~#=]+", views.test, name="test"),
path('admin/', admin.site.urls),
]
views.py
from django.http import HttpResponse
def test(request, obj, field):
print(f"The object is {obj}")
print(f"The field is {field}")
return HttpResponse("Test test")
When visiting the following URL: /objects/http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1/foo
You get this error:
(I've outlined the relevant part with red.)
Django automatically decodes the encoded URL and only then applies the regex match. objects/http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1/foo becomes objects/http://0.0.0.0:3030/u/?id=c789793d-9538-4a27-9dd0-7bb487253da1/foo. You will have to write the equivalent regex expression that matches against the decoded URL.
Something like this will work:
urls.py
from django.contrib import admin
from django.urls import path, re_path
from . import views
urlpatterns = [
re_path(r"(?P<obj>https?:\/\/.*\?id=[\d\w-]+)\/(?P<field>foo|bar)", views.test, name="test"),
path('admin/', admin.site.urls),
]
views.py
from django.http import HttpResponse
def test(request, obj, field):
print(f"The object is {obj}")
print(f"The field is {field}")
return HttpResponse("Test test")
Visiting the URL /objects/http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1/foo will print the following to the console:
The object is http://0.0.0.0:3030/u/?id=c789793d-9538-4a27-9dd0-7bb487253da1
The field is foo
If I am understanding your issue properly, it looks like you are attempting to get a regex match and immediately send a request to the resultant url?
If that is the case, you are sending the request to an improperly formatted url. The first regex you posted looks like it works just fine to get the result you are asking for, however it results in a url that is still encoded.
You need to "unquote" the url prior to making the request.
import re
from urllib.parse import unquote
path = '/objects/http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1/foo'
resp = re.search("https?[-a-zA-Z0-9%._+~#=]+", path)
url = resp[0]
print(url)
print(unquote(url))
results in and output of:
http%3A%2F%2F0.0.0.0%3A3030%2Fu%2F%3Fid%3Dc789793d-9538-4a27-9dd0-7bb487253da1
http://0.0.0.0:3030/u/?id=c789793d-9538-4a27-9dd0-7bb487253da1
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'))
Summary:
Django is not rendering a basic 2 column by 11 row HTML table containing the top 10 most commonly used words in a large text file (Alice and Wonderland which is a public domain work). But right now the table is empty.
Details:
The table currently rendering on the web page looks like this, see here on imgur. In that pic, notice the bottom right corner, the table is empty.
The fully populated word counter table and its contents render when a web user navigates to http://127.0.0.1:8000/counters. See here on imgur.
However the problem in the second pic is that the blog post 'lorem ipsum' content is empty.
Here is my parent top level urls.py:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# path('', include('mortems.urls')),
path('', include('redactors.urls')),
path('', include('posts.urls')),
path('', include('counters.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Here is my counters app’s views.py:
from django.shortcuts import render
from collections import Counter
import re
from nltk.corpus import stopwords
def counters(request, text=""):
"""
This function processes the top 10 most common words in a text file and then renders them.
"""
text = open("counters/Alice.txt", "r").read().lower()
stoplist = stopwords.words('english')
stoplist.extend(["said","gutenberg", "could", "would",])
clean = []
for word in re.split(r"\W+",text):
if word not in stoplist:
clean.append(word)
top_10 = Counter(clean).most_common(10)
return render(request, 'alls/landings.html', {'top_10': top_10})
Here is my counters app’s urls.py:
from django.urls import path, include
from . import views
urlpatterns = [
path('counters', views.counters, name='counters'),
]
Take note of the first argument inside the path function. That is the problem because now web visitors see the table when they navigate to 127.0.0.1:8000/counters but I am trying to make Django serve the table when the user navigates to 127.0.0.1:8000/or 127.0.0.1:8000/home. So when I change that first ‘counters’ argument to ‘home’ or ‘’ (leaving it blank) then the table doesn’t render at all.
To conclude, what I am trying to do is have Django serve the counters function and pass in the top_ten dictionary when rendering the alls/landings.html template when the website visitor navigates to either http://127.0.0.1:8000/home or http://127.0.0.1:8000/. That is what I am trying to accomplish. But instead right now to see the table, the user needs to visit http://127.0.0.1:8000/counters where the blog post content is absent.
There is no traceback in my Django server logs, so I don’t have very many Google search terms to work with.
By the way, for what it is worth, here is my abridged (reduced) HTML table as it appears inside my templates/alls/landings.html:
<h1>Counting the words!</h1>
This should only show inside landing.html
<div class="">
<table>
<tr><th>Word</th> <th>Quantity</th></tr>
{% for word, count in top_10 %}
<tr> <td>{{word}} </td><td>{{count}} </td></tr>
{% endfor %}
</table>
</div>
Here is a complete static snapshot of my full source code on GitHub.
I think the way you have designed your top level urls is the problem. If you want the user to redirect to your counter view as soon as he/she visits the homepage, you need to change the parent urls.py to something like this:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
# path('', include('mortems.urls')),
path('redactors', include('redactors.urls')),
path('posts', include('posts.urls')),
path('', include('counters.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
The problem in your version is that as soon as a user visits homepage, he is directed towards redactors.urls as that is the first match found for the homepage url.
Next, you will need to change the counters.urls to:
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.counters, name='counters'),
]
This will redirect the user directly towards the counter view when he/she visits your web homepage.
Alternatively, if you want the counter view to be served on http://127.0.0.1:8000/home, then you can change the parent urls.py to:
path('home/', include('counters.urls')),
Eureka! It’s working!
Since last time, counters/views.py has been rearranged. The functions contained inside that view have been moved into the posts/views.py. Even the top_word_counts function has been moved into a new file at posts/utils.py. Here is a screenshot on imgur showing the table with the most commonly appearing words in Alice.txt (bottom left corner): https://imgur.com/XTh1FGL
You might notice traces of new functionality. For example not only does my app count the contents of Alice.txt but now the app also counts the most commonly appearing words inside the blog posts (right now lorem ipsum placeholder content).
With all of those changes in mind, here is my working posts/views.py:
import operator
from django.shortcuts import redirect, render, get_object_or_404
from posts.models import Posts
from .utils import top_word_counts
def posts(request):
posts = Posts.objects.all().order_by('-pub_date')
context = {'posts':posts}
post_string = ''
for post in posts:
post_string += post.body
post_words = top_word_counts(post_string.lower())
alice_words = top_word_counts(
open("counters/Alice.txt", "r").read().lower())
context.update({
'post_words': post_words,
'alice_words': alice_words
})
return render(request, 'alls/landings.html', context)
Here is my new posts/utils.py:
import re
from collections import Counter
from nltk.corpus import stopwords
def top_word_counts(text):
# text = open("counters/Alice.txt", "r").read().lower()
stoplist = stopwords.words('english')
stoplist.extend(["said", "gutenberg", "could", "would", ])
clean = []
for word in re.split(r"\W+", text):
if word not in stoplist:
clean.append(word)
top_10 = Counter(clean).most_common(10)
return top_10
I am following a udemy course where I am making a real estate website using Django. I have come so far and have been fighting with the errors but I am stuck with the pagination error that I am facing right now.
I see everything is working fine in the video but when I follow it exactly it does not work. The issue is that when I set a limit on items per page, it does not seem to be working as I see all the results on the same page.
Here is my views.py
from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Listing
def index(request):
listings = Listing.objects.all().order_by('-id')
paginator = Paginator(listings, 3)
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
context = {
'listings': listings
}
return render(request, 'listings/listings.html', context)
It's my Urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='listings'),
path('<int:listings_id>', views.listing, name='listing'),
path('search', views.search, name='search')
As you can see, I have set maximum 3 listings per page but I am getting all of them on a single page which is not what I want. It would be great if anyone could help. Thanks for your time and efforts.
You are returning whole listings instead of Paginator page inside of context
context = {
'listings': page_obj
}
More regarding pagination you can check also in documentation
It's probably a stupid question but its really late here and my brain died after my 6th coffee.
I'm building (or trying to) a simple blogging application that would display an article index on the homepage - aka. recent articles - and on the main blog page. To do so I've managed to scribble out a following view:
def index(request):
'''Article index'''
archive_dates = Article.objects.datetimes('date_publish','month', order='DESC')
categories = Category.objects.all()
page = request.GET.get('page')
article_queryset = Article.objects.all()
paginator = Paginator(article_queryset, 5)
try:
articles = paginator.page(page)
except PageNotAnInteger:
#If page requested is not an integer, return first page.
articles = paginator.page(1)
except EmptyPage:
#If page requested is out of range, deliver last page of results.
articles = paginator.page(paginator.num_pages)
return render(
request,
'blog/article/index.html',
{
'articles': articles,
'archive_dates': archive_dates,
'categories': categories
}
)
However to display the index within two different URLs I've copied the code changing only few variables, ie. name and template to render.
What could I do to render this view within both URLs yet not duplicate it in my views.py?
Am I right in thinking that I'd have to have 3 views, a main one and two sub ones that would import the code from the main one?
Or should I be using a custom template tag instead?
EDIT
As requested, adding urls.py
from django.conf.urls import *
from django.contrib import admin
from settings import MEDIA_ROOT
from django.views.generic import TemplateView
from blog.views import *
admin.autodiscover()
urlpatterns = patterns('',
#Blog URLs
url('^$', home_index, name='blog-preview'),
url('^blog/archive/(?P<year>[\d]+)/(?P<month>[\d]+)/$', date_archive, name='blog-date-archive'),
url('^blog/archive/(?P<slug>[-\w]+)/$', category_archive, name='blog-category-archive'),
url('^blog/categories/', category_list, name='blog-category-list' ),
url('^blog/(?P<slug>[-\w]+)/$', single, name='blog-article-single'),
url('^blog/$', index, name='blog-article-index'),
url(r'^contact/', include("contact_form.urls", namespace="contact_form")),
url(r'^admin/', include(admin.site.urls)),
)
It's very simple: map two urls in your conf to the view like this:
urlpatterns = patterns('',
url(r'first_expression', index, name='first'),
url(r'second_expression', index, name='second'),
)
Also, a little advise on your code: try to avoid wildcard imports. They are dangerous...
Insead use:
from package import MyClass, my_function, my_etc