What i am trying to achieve:
i am getting data from an API, i want to display that data in my template only if the conditions are met.
What i tried #1:
i added a boolean in my model that is linked to the API called "accessconfirmed" and then
in my views i added a if statement saying if access is confirmed then display this context and if not then display this other context. i dont get an error but my data from the API is no longer displaying, i get nothing. here is the code in the views.py
VIEWS:
def projectdetailscoins(request, pk):
coin = Coin.objects.get(id=pk)
notifications = Notification.objects.all()
accessconfirmed = Coin.is_api_access
api_coin_number_variable = coin.api_coin_number
url = 'XXX'
parameters = {
'slug': coin.api_slug,
'convert': 'USD',
}
headers = {
'Accepts': 'application/json',
'X-CMC_PRO_API_KEY': 'XXX'
}
session = Session()
session.headers.update(headers)
response = session.get(url, params=parameters)
api_price = response.json()
if accessconfirmed == True:
context = {'coin':coin, 'notifications':notifications,
'gimmeprice':api_price['data'][api_coin_number_variable]['quote']['USD']['price'],
'gimme24h':api_price['data'][api_coin_number_variable]['quote']['USD']['percent_change_24h'],
'accessconfirmed':accessconfirmed,
}
else:
context = {'coin':coin, 'notifications':notifications,}
return render(request, 'blog/project_details_coin.html', context)
What i tried #2:
i also tried changing the if statement from
if accessconfirmed == True:
to
if accessconfirmed:
the data displays correctly on my template if i do that, but when i go to the PK without "accessconfirmed"
i get this error
KeyError
Exception Type: KeyError
Exception Value: 'data'
line 62 - ...'gimmeprice':api_price['data'][api_coin_number_variable]['quote']['USD']['price'],....
please advise what I am doing wrong
FIXED IT.
i realized that the if statement was just checking if access was confirmed instead of checking if access was confirmed for every individual object so i switched it from
if accessconfirmed:
to
if coin.is_api_access:
and everything is good now, leaving this up incase someone runs in to similar issues
Related
, I am working on this project which is of an e-commerce site using django. I created a view which will process the order of the user with respect to different scenarios if the user is authenticated or not.
Here is the code for the view.
transaction_id = datetime.datetime.now().timestamp()
data = json.loads(request.body)
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
else:
customer, order = guestOrder(request, data)
total = float(data['form']['total'])
order.transaction_id = transaction_id
if total == order.get_cart_total:
order.complete = True
order.save()
if order.shipping == True:
ShippingAddress.objects.create(
customer=customer,
order=order,
address=data['shipping']['address'],
city=data['shipping']['city'],
state=data['shipping']['state'],
zipcode=data['shipping']['zipcode'],
)
return JsonResponse('Payment submitted..', safe=False)
Note: there are some functions which are in some other files if you want to check them do let me know.
enter image description here
in this image there are two bugs the first one is referring to process_order view (code above).
i am not able to resolve this issue.
and when i am trying to POST the data to the database
var url = "/process_order/"
fetch(url, {
method:'POST',
headers:{
'Content-Type':'applicaiton/json',
'Accept': 'application/json',
'X-CSRFToken':csrftoken,
},
body:JSON.stringify({'form':userFormData, 'shipping':shippingInfo}),
})
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
alert('Transaction completed');
with this code also it in not sending data. Can anybody help?
Edit: This is not the whole story. The server also produces the 500 error, and this is most probably an HTML code with cannot be parsed by response.json() either. You should check the error in the python console and resolve it. You can also try looking at the browser dev tools to see the whole response produced by Django.
It's not clear why the error states token 'K', probably it's from the not shown part of the code, but the issue lies, well, in the invalid JSON response produced by the server.
You cannot have a plain string as a top-level thing in the JSON specification. So instead of JsonResponse('Payment submitted..') you should use something like this:
JsonResponse({'detail': 'Payment submitted..'})
Same for all other instances of JsonResponse usage.
I have a url, http://127.0.0.1:8000/lesson/riff-lab/1305/pentab-wow/
When a user navigates to the above url, I want to change it to http://127.0.0.1:8000/lesson/riff-lab/1305/pentab-wow/?d:a3ugm6eyko59qhr/pentab-Track_1.js
The appended part is needed in order to load something that I want to load, but the specifics are not important for this question.
Here's what I have tried.
def my_view(request, pk):
context = {}
page = Page.objects.get(pk=pk)
request.GET._mutable = True
request.GET['?d:%s/%s' % (page.dropbox_key, page.dropbox_js_file_name)] = ""
return render(request, template, context)
Also
def my_view(request, pk):
context = {}
page = Page.objects.get(pk=pk)
request.GET = request.GET.copy()
request.GET['?d:%s/%s' % (page.dropbox_key, page.dropbox_js_file_name)] = ""
return render(request, template, context)
These do not change the url.
Can anyone help? Thanks in advance.
You trying to change the aim of a shell that already has hit it's target.
URL comes first, then view routed to it is processed, there is no way to change it without making another request, e.g. returning a redirect response "please open this url now" to the client.
You can easily find it in django docs by this keywords, but what you are trying to do generally doesn't look very reasonable, if you know beforehand how you need to construct url, why change it mid-way or why change it at all if view has all required data? I don't know your context, but it's probable that you need to reconsider your approach.
I'm using Django Rest Framework to serve an API. I've got a couple tests which work great. To do a post the user needs to be logged in and I also do some checks for the detail view for a logged in user. I do this as follows:
class DeviceTestCase(APITestCase):
USERNAME = "username"
EMAIL = 'a#b.com'
PASSWORD = "password"
def setUp(self):
self.sa_group, _ = Group.objects.get_or_create(name=settings.KEYCLOAK_SA_WRITE_PERMISSION_NAME)
self.authorized_user = User.objects.create_user(self.USERNAME, self.EMAIL, self.PASSWORD)
self.sa_group.user_set.add(self.authorized_user)
def test_post(self):
device = DeviceFactory.build()
url = reverse('device-list')
self.client.force_login(self.authorized_user)
response = self.client.post(url, data={'some': 'test', 'data': 'here'}, format='json')
self.client.logout()
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
# And some more tests here
def test_detail_logged_in(self):
device = DeviceFactory.create()
url = reverse('device-detail', kwargs={'pk': device.pk})
self.client.force_login(self.authorized_user)
response = self.client.get(url)
self.client.logout()
self.assertEqual(status.HTTP_200_OK, response.status_code, 'Wrong response code for {}'.format(url))
# And some more tests here
The first test works great. It posts the new record and all checks pass. The second test fails though. It gives an error saying
AssertionError: 200 != 302 : Wrong response code for /sa/devices/1/
It turns out the list view redirects the user to the login screen. Why does the first test log the user in perfectly, but does the second test redirect the user to the login screen? Am I missing something?
Here is the view:
class APIAuthGroup(InAuthGroup):
"""
A permission to allow all GETS, but only allow a POST if a user is logged in,
and is a member of the slimme apparaten role inside keycloak.
"""
allowed_group_names = [settings.KEYCLOAK_SA_WRITE_PERMISSION_NAME]
def has_permission(self, request, view):
return request.method in SAFE_METHODS \
or super(APIAuthGroup, self).has_permission(request, view)
class DevicesViewSet(DatapuntViewSetWritable):
"""
A view that will return the devices and makes it possible to post new ones
"""
queryset = Device.objects.all().order_by('id')
serializer_class = DeviceSerializer
serializer_detail_class = DeviceSerializer
http_method_names = ['post', 'list', 'get']
permission_classes = [APIAuthGroup]
Here is why you are getting this error.
Dependent Libraries
I did some searching by Class Names to find which libraries you were using so that I can re-create the problem on my machine. The library causing the problem is the one called keycloak_idc. This library installs another library mozilla_django_oidc which would turn out to be the reason you are getting this.
Why This Library Is Causing The Problem
Inside the README file of this library, it gives you instructions on how to set it up. These are found in this file. Inside these instructions, it instructed you to add the AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = [
'keycloak_oidc.auth.OIDCAuthenticationBackend',
...
]
When you add this authentication backend, all your requests pass through a Middleware defined inside the SessionRefresh class defined inside mozilla_django_oidc/middleware.py. Inside this class, the method process_request() is always called.
The first thing this method does is call the is_refreshable_url() method which always returns False if the request method was POST. Otherwise (when the request method is GET), it will return True.
Now the body of this if condition was as follows.
if not self.is_refreshable_url(request):
LOGGER.debug('request is not refreshable')
return
# lots of stuff in here
return HttpResponseRedirect(redirect_url)
Since this is a middleware, if the request was POST and the return was None, Django would just proceed with actually doing your request. However when the request is GET and the line return HttpResponseRedirect(redirect_url) is triggered instead, Django will not even proceed with calling your view and will return the 302 response immediately.
The Solution
After a couple of hours debugging this, I do not the exact logic behind this middleware or what exactly are you trying to do to provide a concrete solution since this all started based off guess-work but a naive fix can be that you remove the AUTHENTICATION_BACKENDS from your settings file. While I feel that this is not acceptable, maybe you can try using another library that accomplishes what you're trying to do or find an alternative way to do it. Also, maybe you can contact the author and see what they think.
So i guess you have tested this and you get still the same result:
class APIAuthGroup(InAuthGroup):
def has_permission(self, request, view):
return True
Why do you use DeviceFactory.build() in the first test and DeviceFactory.create() in the second?
Maybe a merge of the two can help you:
def test_get(self):
device = DeviceFactory.build()
url = reverse('device-list')
response = self.client.get(url)
self.assertEqual(status.HTTP_200_OK, response.status_code)
Is this a problem with the setUp() method? From what I see, you may be setting self.authorize_user to a user that was already created on the first test.
Instead, I would create the user on each test, making sure that the user doesn't exist already, like so:
user_exists = User.objects.filter(username=self.USERNAME, email=self.EMAIL).exists()
if not user_exists:
self.authorize_user = User.objects.create_user....
That would explain why your first test did pass, why your second didn't, and why #anupam-chaplot's answer didn't reproduce the error.
Your reasoning and code looks ok.
However you are not giving the full code, there must be error you are not seeing.
Suspicious fact
It isn't be default 302 when you are not logged in.
(#login_required, etc redirects but your code doesn't have it)
Your APIAuthGroup permission does allow GET requests for non-logged-in user ( return request.method in SAFE_METHODS), and you are using GET requests (self.client.get(url))
So it means you are not hitting the endpoint that you think you are hitting (your get request is not hitting the DevicesViewSet method)
Or it could be the case you have some global permission / redirect related setting in your settings.py which could be DRF related..
eg :
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
Guess
url = reverse('device-detail', kwargs={'pk': device.pk})
might not point to the url you are thinking..
maybe there's another url (/sa/devices/1/) that overrides the viewset's url. (You might have a django view based url)
And I didn't address why you are getting redirected after force_login.
If it's indeed login related redirect, all I can think of is self.authorized_user.refresh_from_db() or refreshing the request ..
I guess some loggin related property (such as session, or request.user) might point to old instance .. (I have no evidence or fact this can happen, but just a hunch) and you better off not logging out/in for every test case)
You should make a seperate settings file for testing and add to the test command --settings=project_name.test_settings, that's how I was told to do.
I'm trying to check if email id entered by user is existing in the database table, if existing - I would like to route to 'prof.html' template otherwise just show a message in the login.html template.
Both the conditions are working fine.
However, the problem is when I use redirect() or render_to_response() -
the destination template elements like div, input etc., are being changed automatically (prof.html in this case) ?
Can we also send the context information to destination template ?
(response data or any object from the database and redirect to prof.html template via view in this case)
Below is my code :
Views.py
def verifyme(request):
if request.method == "POST":
emailid4loginV = request.POST['emailid4login_Aj']
else:
emailid4loginV = ''
response_data = ''
return HttpResponse(response_data, content_type="text/plain")
response_data = ''
if Employee.objects.filter(email = emailid4loginV).exists():
response_data='Thanks for waiting - login successful'
#return render_to_response('app/prof.html', { 'response_data':response_data},
# context_instance = RequestContext( request ) )
return redirect('/myprofile')
else:
response_data='Ouch! you are not a registered user!'
return HttpResponse(response_data, content_type="text/plain")
urls.py
url(r'^myprofile$', 'app.views.profile', name='profile'),
Just for your info, 'profile' view does return some objects from the table and renders in the template app/prof.html.
I observed that the destination template is being rendered in same login.html template (How ? : In the browser url, I dont see myprofile - but the one for login) But when I request the myprofile manually by entering in the website url (localhost:xxxxx/myprofile), it works perfectly :(
URL before submitting request in login.html :
URL after submitting request in login.html - myprofile is rendered in the same page :
When I manually type in the url, template just works perfectly..
Could you please let me know what could be the problem ?
EDIT:
Solved this issue with a little trick, posted in the below
https://stackoverflow.com/questions/31091938/why-is-httpresponseredirectreverse-doesnt-redirect-to-new-page
1) Actually there are many ways to pass data to next view ... generally in such cases like you have better way - using sessions (cookie|localstorage|sessionstorage), it is like clipboard ... save session data in one view and get it later in another one. For example:
First view:
self.request.session['response_data'] = 'some text'
self.request.session.set_expiry(0) # user’s session cookie will expire when the user’s Web browser is closed.
Other views:
response_data = self.request.session.get('response_data', '')
But if you planning just use this data in template Django has some kind more high-level interface for it and in your case semantically right to use it - The messages framework https://docs.djangoproject.com/en/1.8/ref/contrib/messages/
2) If you want redirect to another view better use url namespaces and reverse https://docs.djangoproject.com/en/1.8/ref/urlresolvers/#reverse
return HttpResponseRedirect(reverse(app.views.profile)) # here I've passed callable object because you have not show your app url namespace, but generally use namespaces
https://docs.djangoproject.com/en/1.8/topics/http/urls/#url-namespaces
I am currently using Django forms to have users enter information, which I then use Django sessions to call in other view functions in my views.py file. Currently my form is processed in the view function, 'search' and is called using sessions in latter view functions. However, when I enter data into my form and submit it, I get the error:
cannot concatenate 'str' and 'NoneType' objects
Here is my code, thus far:
def search(request):
t = request.session.get("tick")
if request.method == 'POST':
search = Search(data=request.POST)
if search.is_valid():
success = True
ticker = search.cleaned_data['search']
request.session["tick"] = ticker
else:
print search.errors
else:
search = Search()
def search_overview(request):
result = {}
context = RequestContext(request)
t = request.session.get("tick")
sourceCode = urllib2.urlopen("http://finance.yahoo.com/q/ks?s="+t).read()
pbr = sourceCode.split('Price/Book (mrq):</td><td class="yfnc_tabledata1">')[1].split('</td>')[0]
result['pbr'] = pbr
return render_to_response('ui/search.html', {"result":result}, context)
Anyone have any ideas on how I can fix this, so that I can use sessions to store data touse in different view functions? Thanks.
In your search_overview function, t is getting set to None. Which cannot be concatenated with a string.
You could try this as a quick fix, but it doesn't solve the issue of tick not being in the session:
t = request.session.get('tick', '')
This returns the value for the tick key in session if it exists, otherwise it returns an empty string. Which can be concatenated to another string.