list index out of range error from previous view when rendering a different view - django

Apologies if I'm missing something daft here but I'm new to Django and I can't work this out.
I'm creating a basic reddit style app focusing on cryptocurrency. I have a view which gets price data from an API and displays it as well as any posts specific to that coin:
views.py:
def coin_posts(request, id):
if request.method == 'GET':
coin_display = {}
post = Post.objects.filter(coin_name=id)
api = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=%s&order=market_cap_desc&per_page=100&page=1&sparkline=false' % id
response = requests.get(api)
data = response.json()
coin = data[0]
coin_data = Coins(
coin_id = coin['id'],
coin_name = coin['name'],
coin_price = coin['current_price'],
market_cap = coin['market_cap'],
rank = coin['market_cap_rank'],
price_change = coin['price_change_24h'],
slug = coin['symbol'],
image_url = coin['image']
)
coin_data.save()
coin_display = Coins.objects.filter(coin_id=id)
return render(request, 'coins.html', {
'post': post,
'coin_display': coin_display,
'post_form': PostForm()
},
)
I want to be able to click on each post to view any comments related to said post, as I haven't got that far yet I just wish to be able to view that single post on another page. So each post has the following link on the template:
Comments
and here are the corresponding URLs and view:
urlpatterns = [
path('', views.index, name="index"),
path('<str:id>/', views.coin_posts, name='coin_posts'),
path('<slug:slug>/', views.post_detail, name='coin_detail'),
]
def post_detail(request, slug):
post = Post.objects.filter(slug=slug)
return render(request, 'coin_detail.html', {
'post': post,
},
)
The problem is when I click on the link, I get the error: list index out of range
Which is referencing the coin = data[0] in the first view.
I can see that it's trying to populate the database, but because there's no GET data there's no list to index. My question is why is that happening? If I've clicked a link which calls the post_detail function, why is it executing the code in the coin_posts function?

I think the problem is in the url paths. The path of coin_posts "str:id/" is equal to the path of post_detail 'slug:slug/'. The only difference between these two urls is that str will accept only string while slug will match any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters (reference).
Try defferenciating the path adding a different path for example:
urlpatterns = [
path('', views.index, name="index"),
path('coin_posts/<str:id>/', views.coin_posts, name='coin_posts'),
path('post_detail/<slug:slug>/', views.post_detail, name='coin_detail'),
]

Related

django - No User matches the given query. Page Not found 404: User post list view breaks post detail view

I'm fairly new to django and trying to build a message board app. Everything's been running smoothly up until I tried to add functionality to display a list of posts by author. I got it working but then my post detail view started throwing a 'No User matches the given query. Page Not found 404' error and I can't seem to figure out why. Can anyone help?
views.py
class UserPostList(generic.ListView):
model = Post
# queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'user_posts.html'
paginate_by = 6
def get_queryset(self):
"""
Method to return posts restricted to 'published' status AND to
authorship by the user whose username is the parameter in the
url.
"""
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(
status=1, author=user
).order_by('-created_on')
class FullPost(View):
def get(self, request, slug, *args, **kwargs):
"""
Method to get post object.
"""
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.order_by('created_on')
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"full_post.html",
{
"post": post,
"comments": comments,
"liked": liked
},
)
# I'll be adding a comment form in here too
urls.py
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
Error message
(When trying to view a single post (previously working) after adding the UserPostList view and route)
Using the URLconf defined in mhcmsgboard.urls, Django tried these URL patterns, in this order:
1. admin/
2. summernote/
3. register/ [name='register']
4. profile/ [name='profile']
5. login/ [name='login']
6. logout/ [name='logout']
7. new/ [name='create_post']
8. <slug:slug>/update/ [name='update_post']
9. <slug:slug>/delete/ [name='delete_post']
10. <str:username>/ [name='user_posts']
The current path, test-post/, matched the last one.
for <str:name> in path "-" not allowed to use in name,
when you use Slug both paths are equal.
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<slug:username>/', views.UserPostList.as_view(), name='user_posts'),
]
there are 2 simple ways
use sub path for one or both paths : user_post/<slug:username>/
Use re_path and define the regex to change path
exp:
re_path(r'^(?P\<username>\w+)/$', views.UserPostList.as_view()),
The problem is that you match update_post, delete_post and user_posts URL's to the root. As the error message explains, the current request is made against user_posts. And it seems that you don't have a user called test-post.
You could solve it e.g. with the following URL conf:
urlpatterns = [
...
path('board/<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('posts/<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
That way, each path is unique and Django knows which View it has to call.

How to perform a query by using URL with question mark in Django?

It seems like the original URL querying function has been removed from Django 3.1. Does anyone know how to do it with a new package?
The url.py:
urlpatterns = [
re_path(r'^portfolio/(?P<title>[\w-]+)/$' , BlogApp_View.displayPortfolio, name='displayPortfolio'),
path('portfolio/', BlogApp_View.selectPortfolio, name='selectPortfolio'),]
The view.py
def displayPortfolio(request):
title = request.GET.get('title')
portfolio = Article.objects.filter(articleType__name__contains = "Portfolio", title=title)
print(title)
DICT = {}
return render(request, 'Article/', DICT)
The problem is now if I visit http://127.0.0.1:8000/Blog/portfolio/?title=A_UAV_Positioning_Approach_Using_LoRa/, it will skip the re_path shows in url.py.
Instead, it goes to the path one.
I have tried str:title method but that is actually not what I want. I prefer using the question mark pattern to finish the query.
The part after the questionmark is the querystring [wiki] and is not part of the path. This thus means that regardless what patterns you write, you can not distinguish on this, since the path patterns, regardless whether it is a path or re_path, are never matched against a URL with a query string.
You thus should write a single view, and inspect the request.GET query dict (which is a dictionary-like representation of the query string and see if it contains a value for title.
Your urlpatterns thus look like:
urlpatterns = [
path('portfolio/', BlogApp_View.selectPortfolio, name='selectPortfolio'),
]
and in the view, you can see if it contains a title:
def selectPortfolio(request):
if 'title' in request.GET:
# contains a ?title=…
title = request.GET.get('title')
portfolio = Article.objects.filter(
articleType__name__contains='Portfolio',
title=title
)
data = {'portfolio': portfolio}
return render(request, 'some_template.html', data)
else:
# contains no ?title=…
# …
return …

Page view refers to id, whil path is not asking for one

I want to load a default django page. Nothing fancy. However, the error I get, hints at an id that is incorrectly set.
"Field 'id' expected a number but got 'zoekboek'."
The confusing things here (I am a django beginner, so I wouldn't be surprised if this is not confusing at all for you):
the path for this page in the urls.py is not asking for an id.
the view is not querying anything yet (I found some posts that had similar errors,
but related to a filter).
the debug info points to another view that indeed is requesting an id.
when I add a slash at the beginning of the path, the error is gone!
The code
urls.py
urlpatterns = [
path('', views.scholen, name='scholen'),
path('<school_id>', views.school_detail, name='school_detail'),
path('<school_id>/<groep_id>', views.school_groep, name='school_groep'),
path('<school_id>/<groep_id>/<UserProfile_id>', views.leerling_page, name='leerling_page'),
path('zoekboek', views.zoekboek, name='zoekboek'),
]
views.py
from django.shortcuts import render, redirect, reverse, get_object_or_404
from books.models import Book, Rating
from .models import School, Groep
from profiles.models import UserProfile, Hobby, Sport
from django.contrib.auth.models import User
# Create your views here.
def scholen(request):
"""
Homepage for participating
schools.
"""
scholen = School.objects.all()
context = {
'scholen': scholen,
}
return render(request, 'schools/school_landing.html', context)
def school_detail(request, school_id):
"""
Details of individual schools.
"""
school = get_object_or_404(School, pk=school_id)
groep = Groep.objects.filter(school=school)
context = {
'school': school,
'groep': groep,
}
return render(request, 'schools/school_detail.html', context)
def school_groep(request, school_id, groep_id):
"""
Details of groep.
"""
school = get_object_or_404(School, pk=school_id)
groep = get_object_or_404(Groep, pk=groep_id)
a = groep.naam
kinderen = UserProfile.objects.filter(groep=a)
context = {
'school': school,
'groep': groep,
'kinderen': kinderen,
}
return render(request, 'schools/school_groep.html', context)
def leerling_page(request, school_id, groep_id, UserProfile_id):
"""
Personal page of school kids.
"""
profile = get_object_or_404(UserProfile, pk=UserProfile_id)
# If viewer is owner of page, viewer can edit
owner = False
if request.user == profile.user:
owner = True
context = {
'profile': profile,
'owner': owner,
}
return render(request, 'schools/leerling_page.html', context)
def zoekboek(request):
"""
Page for kids to search their favorite book
"""
context = {
}
return render(request, 'schools/zoek_boek.html', context)
Is this enough information?
Simple fix: move path('zoekboek', views.zoekboek, name='zoekboek'), from the last place to the second place in your urls.
Why?
Because Django URLs are resolved using regular expressions; the docs say here in point 3:
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info.
Since your URL path path('<school_id>', views.school_detail, name='school_detail'), is very generic, it matches any string including the string zoekboek; so the request to zoekboek falls into the second line in your URL conf and gets routed to the view school_detail() and a school_id is expected for that view.
Suggestion: to make the URL handling easier and so you can order the URL paths however you like, you could change the URL a bit and add a prefix (for example school/) so that not any string matches the URL paths. For example, this schould work:
urlpatterns = [
path('', ...),
path('school/<school_id>', ...),
path('school/<school_id>/<groep_id>', ...),
path('school/<school_id>/<groep_id>/<UserProfile_id>', ...),
path('zoekboek', ...),
]

django redirect causes a redirect to next page, but then returns to original?

My redirect function is causing some issues. I call a url from a view using reverse with the parameters required for the view. There are no errors and in the browser of the url it correctly displays these parameters. However it seems like it redirects to the new url, but immediately after requesting the new view for the new url the page returns to the original view with the new url still displayed in the browser. Can anyone tell me if I am using the redirect function correctly or maybe I am using the reverse incorrectly?
P.S. I chopped out a lot of code because StackOverflow won't let me post all of it.
home/urls.py
from django.conf.urls import url
from home import views
app_name = 'home'
urlpatterns = [
url('^$', views.index, name='index'),
url('^patient_summary/patientid=(?P<patient_id>\d+)&clinicid=(?P<clinic_id>\d+)/', views.patient_summary, name='patient_summary'),
url('^patient_summary/patientid=(?P<patient_id>\d+)&clinicid=(?P<clinic_id>\d+)/careplanid=(?P<careplan_id>\d+)/', views.care_plan, name='care_plan'),
]
home/views.py
def patient_summary(request, token, patient_id, clinic_id):
user = get_user(token)
if request.method == "POST":
if ('careplanselected' in request.POST):
props = request.POST.get('careplan')
props = props.split("#")
CPID = props[0]
cpname = props[1]
my_dict = {'token': token, 'patient_id': patient_id, 'clinic_id': clinic_id, 'careplan_id': CPID}
return redirect(reverse('home:care_plan', kwargs=my_dict))
return render(request, 'home/patient_summary.html')
def care_plan(request, token, patient_id, clinic_id, careplan_id):
user = get_user(token)
care_plan = []
cpname = ''
return render(request, 'home/care_plan.html' )
Your URL patterns are missing dollars to mark the end of the URL. That means that your patient_summary view will be handling requests meant for the care_plan view.
Change the patterns to:
url('^patient_summary/patientid=(?P<patient_id>\d+)&clinicid=(?P<clinic_id>\d+)/$', views.patient_summary, name='patient_summary'),
url('^patient_summary/patientid=(?P<patient_id>\d+)&clinicid=(?P<clinic_id>\d+)/careplanid=(?P<careplan_id>\d+)/$', views.care_plan, name='care_plan'),

How to redirect url with added url parameters

I have a workout calendar app where if a user goes to /workoutcal, I want them to be redirected to workoutcal/<today's year>/<today's month>. That means I want them to be redirected to this route, should they go to /workoutcal:
url(r'^(?P<year>[0-9]+)/(?P<month>[1-9]|1[0-2])$', views.calendar, name='calendar'),
So how can I write a new url pattern in urls.py that does something like:
url(r'^$', RedirectView().as_view(url=reverse_lazy(),todays_year, todays_month)),
You can subclass RedirectView, override get_redirect_url, and reverse the url there.
class MonthRedirectView(RedirectView):
def get_redirect_url(*args, **kwargs):
today = timezone.now()
return reverse(calendar, args=[today.year, today.month])
Then include your view in the URL pattern:
url(r'^$', MonthRedirectView().as_view()),
I found another solution to my problem, which is making a view that simply calls the calendar with the right arguments:
the url:
url(r'^$', views.redirect_to_calendar),
the redirect view:
def redirect_to_calendar(request):
today = timezone.now()
return calendar(request, year = today.year, month = today.month)
the view we serve to the user:
def calendar(request, year = None, month = None):
## A bunch of server logic
return HttpResponse(template.render(context, request))
Assuming your RedirectView is in your app's urls.py:
url(r'^(?P<year>[0-9]+)/(?P<month>[1-9]|1[0-2])$', views.calendar, name='calendar'),
url(r'^$', RedirectView().as_view(url='{}/{}'.format(todays_year, todays_month)),
EDIT: This assumes that todays_year and todays_month are calculated when a user goes to /workoutcal/, which they aren't (they're loaded at URL load time). See Alasdair's answer.