Django -- Views MultiValueDictKeyError - django

I want to return 4 different versions of the homepage
Homepage with search bar. No data present from API
Homepage with search bar. Data present from API
Homepage with search bar. No data present if request doesn't exist in API
Homepage with search bar. No data present if submit button is hit without any data being entered.
Version two, three and four all work.
However version 1, the homepage without a GET request is not returned. Due to:
MultiValueDictKeyError at / 'city'" in the views.py file.
How can this be resolved? Any help will be greatly appreciated
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
]
views.py
from django.shortcuts import render
import requests
def index(request):
# Query API with user input
payload = {'q': request.GET['city'], 'appid': 'API-KEY'}
response = requests.get('http://api.openweathermap.org/data/2.5/weather', params=payload)
# successful request
if response.status_code == 200:
# Parse json output for key value pairs
e = response.json()
context = {
'city_name': e['name'],
'weather':e['weather'][0]['main'],
'description' : e['weather'][0]['description'],
'temp' : e['main']['temp'],
'pressure':e['main']['pressure'],
'humidity':e['main']['humidity'],
'visibility':e['visibility'],
'wind_speed':e['wind']['speed'],
'wind_deg':e['wind']['deg']
}
return render(request, 'index.html', {'context': context})
else: # returns homepage if invalid city name is given in form
return render(request, 'index.html')

Instead of calling request.GET['city'] directly, check if city is set first, like:
if 'city' in request.GET:
payload = {'q': request.GET['city'], 'appid': 'API-KEY'}

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.

Django - Get response to return image without saving file to models

I am using the tmdb api to return movie info as well as images.
Steps below of Get logic
Api request is made which provides movie info as well as "backdrop_path"
I then use this path to make another request for the jpg related to that movie.
Blocker
I'm unable to then output that jpg. It currently returns a url path as below.
Views.py
from django.shortcuts import render
from django.views.generic import TemplateView
import requests
import urllib
# Create your views here.
def index(request):
# Query API with user input
if 'movie' in request.GET:
api_key = 'api'
id = request.GET['movie']
url = 'https://api.themoviedb.org/3/search/movie?api_key={}&language=en-US&query={}&include_adult=false'
response = requests.get(url.format(api_key,id))
# successful request
if response.status_code == 200:
# Parse json output for key value pairs
tmdb = response.json()
# save image jpg
backdrop_path = tmdb['results'][0]['backdrop_path']
url = 'https://image.tmdb.org/t/p/original/{}'
gg = urllib.request.urlretrieve(url.format(backdrop_path), 'test.jpg')
context = {
'title': tmdb['results'][0]['original_title'],
'overview': tmdb['results'][0]['overview'],
'release_date': tmdb['results'][0]['release_date'],
'vote_average': tmdb['results'][0]['vote_average'],
'vote_count': tmdb['results'][0]['vote_count'],
'backdrop_path' : tmdb['results'][0]['backdrop_path'],
'jpg' : gg
}
return render(request, 'home.html', {'context': context})
else: # returns homepage if invalid request
return render(request, 'home.html')
else: # Homepage without GET request
return render(request, 'home.html')
urlretrieve doesn't return the image ready to be used, instead it returns a tuple with the local name of the file and the headers (as an HTTPMessage object as you can see in your example), which is what you're seeing.
However, I don't think returning a file in your response is ideal, nor it would work in your scenario. Since you seem to be using templates, what I would do is return the image url and use it in an image HTML tag, like this <img src="{{ jpg }}"/>

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

django paypal notify_url not configured correctly or working

I did a pip install for django paypal. The button renders and the payment goes through. The return url works too but the notify_url part is not working. So I cannot update my database that a sale has gone through.
I don't get any errors either so I am stumped. If someone can help Id really appreciate it. Thanks
Edit: I added from paypal.standard.ipn.signals import payment_was_successful to the top of my views page. and changed the name of my notify_url to show_me_the_money (Not sure if that matters) I got it from a blog called http://djangodersleri.blogspot.ie/2013/11/paypal-ipn-with-django.html. But the good thing is now at least I seem to be getting my transactions recorded in the table paypal_ipn. But that's all! So still dont know if the show_me_the_money view is being executed.
Here is what I have..
Settings...
INSTALLED_APPS = (
...
'paypal.standard.ipn',
)
PAYPAL_RECEIVER_EMAIL = "my_sandbox_test_email_is_here#gmail.com"
PAYPAL_TEST = True
URLS...
(r'^show_me_the_money /', include('paypal.standard.ipn.urls')),
Views...
from paypal.standard.models import ST_PP_COMPLETED
from paypal.standard.ipn.signals import valid_ipn_received
from paypal.standard.ipn.signals import payment_was_successful
def show_me_the_money (sender, **kwargs):
ipn_obj = sender
if ipn_obj.payment_status == ST_PP_COMPLETED:
doc_id=ipn_obj.item_number1 # :document_id,
us_id=ipn_obj.item_number2 #user.id,
obj_doc=Document.objects.get(id=doc_id)
my_user = User.objects.get(id=us_id)
obj_doc.users.add(my_user)
obj_doc.save()
try:
ipn_obj.verify(item_check_callable)
except:
import sys, traceback
traceback.print_exc(file=sys.stdout)
valid_ipn_received.connect(paid)
#csrf_exempt
def myvideos(request):
try:
my_vids=Document.objects.filter(users=request.user.id)#request.user.id
except:
return render_to_response(
'myvideos.html',
context_instance=RequestContext(request)
)
#my_vids= Document.objects.filter(users=request.user.id)
return render_to_response(
'myvideos.html',
{'my_vids': my_vids},
context_instance=RequestContext(request)
)
def video(request, document_id):
document = Document.objects.get(id=document_id)
if request.user.id:
d1 =datetime.datetime.now()
t=d1.strftime('%y%m%d%h%m%s')
pp_price = str(document.price)
# What you want the button to do.
paypal_dict = {
"business": settings.PAYPAL_RECEIVER_EMAIL,
"amount": pp_price + ".00",
"item_number1":document_id,
"item_number2":request.user.id,
"item_name": document.name,
"invoice": document.name+t,
"notify_url": "http://wackosmackosite.com/"+ reverse('paypal-ipn'),
"return_url": "http://wackosmackosite.com/myvideos/",
"cancel_return": "http://wackosmackosite.com/video/"+document_id+"/",
}
form = PayPalPaymentsForm(initial=paypal_dict)
context = {"form": form, "document":document }
return render(request, "video.html", context)
else:
return render_to_response('video.html',{'document': document},
context_instance=RequestContext(request))
Here is the The urls from paypal.standard.ipn
from django.conf.urls import url
from paypal.standard.ipn import views
urlpatterns = [
url(r'^$', views.ipn, name="paypal-ipn"),
]
First off #mcastle Thank you so much for your help. But I just couldn't figure out the Django signals.
Ok so what I had to do in the end is go to the paypal.standard.ipn.views file and import my app and call the show_me_the_money view from there at the very bottom of the view just before it returns the http response.
So the notify url in the paypal dict like this...
"notify_url": "http://wackosmackosite.com/show_me_the_money/",
And the url in my urls file is like this..
url(r'^show_me_the_money/', include('paypal.standard.ipn.urls')),
I was able to extract the info I needed to update my database from the arguments passed to show_me_the_money. like this...
def show_me_the_money(sender, **kwargs):
ipn_obj = sender
payStatus=ipn_obj.POST.get('payment_status','')
if payStatus=='Completed':
....
Then in the paypal ipn view
at the top...
from myApp.views import show_me_the_money
At the bottom...
s_m_t_m=show_me_the_money(request, item_check_callable=None)
return HttpResponse("OKAY")
I found this whole set-up very confusing and think the documentaion for me just leaves lots of important stuff out. Anyway This is working perfect now and I just got off the phone with pay pal and they are happy it is configured correctly.
Review Django's documentation on signals. show_me_the_money looks like it needs to be connected to a signal.