Pass arrays from django to js with ajax - django

I'm trying to pass 2 arrays via ajax from my views to my javascript. When I console.log my arrays i get 2 empty ones. I feel like I know what the error is but I can't solve it. I'm going to include my code first, than my toughts.
views.py:
In my first method, I want to pass my data to 2 arrays (dates,weights). In get_data is where I want to send my data to js.
from django.shortcuts import render
from django.contrib import messages
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from users import models
from users.models import Profile
from .forms import WeightForm
import json
dates = []
weights = []
dates_queryset = []
def home(request):
form = WeightForm()
if request.is_ajax():
profile = get_object_or_404(Profile, id = request.user.id)
form = WeightForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return JsonResponse({
'msg': 'Success'
})
dates_queryset = Profile.objects.filter(user=request.user)
dates = dates_queryset.values_list('date', flat=True)
weights = dates_queryset.values_list('weight', flat=True)
return render(request, 'Landing/index.html',{'form':form})
def get_data(request, *args,**kwargs):
data = {
'date': dates,
'weight': weights
}
return JsonResponse(data)
url:
url(r'^api/data/$', get_data, name='api-data'),
Ajax call:
var endpont = '/api/data'
$.ajax({
method: 'GET',
url: endpont,
success: function(data){
console.log(data);
},
error: function(error_data){
console.log("Error");
console.log(error_data);
}
})
Js console.log output:
{date: Array(0), weight: Array(0)}
My toughts:
I think I need to pass both of my arrays to the context, but if I try to pass them like this {'form':form, 'dates':dates, 'weights':weights'} then I get an error that they are referenced before use. I saw another question on Stackoverflow where they put more than 1 value to the context like this.

Your get_data view is doing exactly what it is supposed to.
You've defined the dates and weights arrays in the global scope, and the home view is not changing those global variables, even though they are named the same thing. See this link for a simple demonstration. Python Global Variables
If you use the global keyword in front of "dates" and "weights" in the home view, then those variables would be updated, and get_data should return the populated arrays.

Related

Django redirecting

I can't figure out why my redirection after paying doesn't work.
I'm trying to display the thank_you template once the user has paid via Paypal. My code all works other than the final render of the thank_you template (I receive the email and see the 'Made it' print statement).
Urls:
from django.urls import path
from . import views
app_name = 'checkout'
urlpatterns = [
path('', views.checkout, name='checkout'),
path('thank_you', views.thank_you, name='thank_you'),
path('order_success', views.order_success, name='order_success'),
]
Views.py:
import json
from django.shortcuts import render, redirect, reverse, HttpResponseRedirect
from django.http import JsonResponse
from django.contrib import messages
from django.urls import reverse
from profiles.models import UserProfile
from products.models import Product
from django.views.decorators.http import require_POST
from .models import Order, OrderDetail
from django.core.mail import send_mail
from the_rescuers.settings import DEFAULT_FROM_EMAIL
from templated_email import send_templated_mail
from .forms import OrderForm
def checkout(request):
bag = request.session.get('bag', {})
if not bag:
messages.error(request, "There's nothing in your bag at the moment")
return redirect(reverse('products:products_list'))
order_form = OrderForm()
bag_products = []
for item_id, quantity in bag.items():
product = Product.objects.get(pk=item_id)
name = product.name
id = product.id
bag_products.append({'name': name, 'id': id, 'quantity': quantity})
bag_products = json.dumps(bag_products)
# Attempt to prefill the form with any info the user maintains in
# their profile
if request.user.is_authenticated:
profile = UserProfile.objects.get(user=request.user)
order_form = OrderForm(initial={
'first_name': profile.default_first_name,
'last_name': profile.default_last_name,
'email': profile.default_email,
'phone_number': profile.default_phone_number,
'country': profile.default_country,
'postcode': profile.default_postcode,
'city': profile.default_city,
'street_address_1': profile.default_street_address_1,
'street_address_2': profile.default_street_address_2,
'county': profile.default_county,
})
template = 'checkout/checkout.html'
success_url = '/checkout/order_success'
thank_you = '/checkout/thank_you'
context = {
'order_form': order_form,
'success_url': success_url,
'bag_products': bag_products,
'thank_you': thank_you,
}
return render(request, template, context)
def order_success(request):
"""
View that creates a new object with the JSON data, then redirects to the
thankyou page.
"""
# Take the request, decode it, split it into bag_contents and order_data
# and use this data to create a new order
request2 = request.body
my_json = request2.decode('utf8').replace("'", '"')
json_data = json.loads(my_json)
bag_contents = json_data.get('bagContents')
bag_contents = json.loads(bag_contents)
order_data = json_data.get('jsonData')
order_data = json.loads(order_data)
# Manually fill the user_id field with the user's id
order_data["user_id"] = request.user.id
# Remove the csrf token from the data
order_data.pop("csrfmiddlewaretoken", None)
# Create a new instance of the Order model using the order_data received
order = Order.objects.create(**order_data)
order.save()
# Loop through the bag_contents and save the details in OrderDetail model
for item in bag_contents:
product = Product.objects.get(pk=item['id'])
order_detail = OrderDetail(order=order, product=product,
quantity=item['quantity'])
order_detail.save()
order.update_total()
# Create a value to check in the thank_you view
request.session['redirected_from_order_success'] = True
print("Original: ", request.session)
# Send email to the provided email address
send_templated_mail(
template_name='order_confirmation',
from_email=DEFAULT_FROM_EMAIL,
recipient_list=[order.email],
context={'name': order.first_name,
'order_number': order.order_number,
'order_total': order.order_total,
},
)
return HttpResponseRedirect(reverse('checkout:thank_you'))
def thank_you(request):
"""
View that displays the thankyou page after processing an order.
"""
# Redirect to the custom 404 page if trying to access the page without
# making an order
if request.session.get('redirected_from_order_success'):
# Clear the bag and redirection token now that the order has been
# created
request.session.pop('bag', None)
request.session['redirected_from_order_success'] = False
print("Made it: ", request.session)
return render(request, 'checkout/thank_you.html')
else:
print("Diverted it: ", request.session)
return render(request, "404.html")
Relevant Checkout.html Javascript:
function completeOrder(){
let url = '{{ success_url }}'
const request= fetch(url, {
method: 'POST',
headers:{
'Content-type':'application/json',
'X-CSRFToken': csrftoken,
},
body:JSON.stringify({"bagContents": bagContents, "jsonData": jsonData} )
})
}
onApprove: (data, actions) => {
return actions.order.capture().then(function (orderData) {
const transaction = orderData.purchase_units[0].payments.captures[0];
return completeOrder()})
}
What's confusing is that the GET request for the thank_you template is made and gives a 200, it just doesn't move from the checkout page?
[24/Jan/2023 08:43:02] "POST /checkout/order_success HTTP/1.1" 302 0
Made it: <django.contrib.sessions.backends.db.SessionStore object at 0x7f75867dcb50>
[24/Jan/2023 08:43:03] "GET /checkout/thank_you HTTP/1.1" 200 6287
Any help would be much appreciated!

Best way to do simple API calls between Django server and android application?

I'm building a system where I store a Member model on a Django server, one of the attributes of Member is score which is what I want to change using API calls. My question is what would be the best way to do this? I looked into the Django REST framework but it seems a bit overkill for what I'm trying to do. I've been trying to pass the necessary information through the url using regular expressions but I'm unsure if it will work. Outline of what I need is below
iOS/Android app makes request sending pk and score to add to total
server updates appropriate model instance and returns True/False to app depending if save was successful
You can achieve this by this quite dirty solution:
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('<int:member_id>/<int:score_to_add>/', views.update_score, name='update_score'),
]
views.py
from django.http import HttpResponse
from .models import Member
def update_score(request, member_id, score_to_add):
member = Member.objects.get(pk=member_id)
member.score += score_to_add
try:
member.save
return HttpResponse("True")
except:
return HttpResponse("False")
Also you can respond with Json. Here is alternative views:
Alternative views.py
from django.http import JsonResponse
from .models import Member
def update_score(request, member_id, score_to_add):
member = Member.objects.get(pk=member_id)
member.score += score_to_add
try:
member.save
return JsonResponse({'status': True})
except:
return JsonResponse({'status': False})
But i think Django Rest Framework is a better way to do this.
You can create a view to return JsonResponse. Take example of polls app in django and convert a post view to return a JSON response.
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.http import JsonResponse
from .models import Choice, Question
# Need to disable csrf here
#csrf_exempt
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
# If you want Json requests Or you can use
# received_json_data=request.POST if you are using normal form data request.
received_json_data=json.loads(request.body)
try:
selected_choice = question.choice_set.get(pk=received_json_data['choice'])
except (KeyError, Choice.DoesNotExist):
# If failed
return JsonResponse({'result': False})
else:
selected_choice.votes += 1
selected_choice.save()
return JsonResponse({'result': True})
Although It works but I would still recommend using DRF because what you are doing needs proper REST API and DRF eases a lot of pain points.

KeyError in city name

I have made an small weather web app in django & it is working properly but
when a wrong city name is entered it start showing KeyError page.
from django.shortcuts import render, redirect
from django.contrib import messages
import requests
#search page
def search(request):
return render(request, 'index.html')
#forecast result page
def forecast(request):
c = request.POST['city']
url = 'http://api.openweathermap.org/data/2.5/weather?q={}&appid=7fee53226a6fbc936e0308a3f4941aaa&units=metric'.format(c)
r = requests.get(url)
data = r.json()
weather = {
'description': data['weather'][0]['description'],
'icon': data['weather'][0]['icon'],
'city': c.title(),
'temperature': data['main']['temp']
}
print(r)
return render(request, 'weather.html', {'weather': weather})
On entering wrong city name it is giving KeyError,So I want that instead of giving KeyError django will redirect it to my homepage i.e index.html with an error message below it.
The API will tell you if the city name is not valid.
r = requests.get(url)
if r.status_code == 404:
messages.add_message('City not found')
return redirect('home')
data = r.json()
...
First of all, please do not construct the querysetring yourself: querystrings can not include a lot of characters. You can use Django's QueryDict for that, like:
from django.http import QueryDict
qd = QueryDict(mutable=True)
qd.update(q=c, appid='7fee53226a6fbc936e0308a3f4941aaa', units='metric')
url = 'http://api.openweathermap.org/data/2.5/weather?{}'.format(qd.urlencode())
For cities like 'New York', it will encode this as:
>>> qd.urlencode()
'q=New+York&appid=7fee53226a6fbc936e0308a3f4941aaa&units=metric'
so it replaces the space with a +.
Furthermore you can use try-except here to redirect to a different page, like:
from django.http import QueryDict
from django.shortcuts import redirect
def forecast(request):
try:
city = request.POST['city']
except:
return redirect('name-of-some-view')
qd = QueryDict(mutable=True)
qd.update(q=city, appid='7fee53226a6fbc936e0308a3f4941aaa', units='metric')
url = 'http://api.openweathermap.org/data/2.5/weather?{}'.format(qd.urlencode())
try:
data = r.json()
weather = {
'description': data['weather'][0]['description'],
'icon': data['weather'][0]['icon'],
'city': c.title(),
'temperature': data['main']['temp']
}
except KeyError:
return redirect('name-of-some-view')
return render(request, 'weather.html', {'weather': weather})
You can use the Django Messages Framework [Django-doc] to show messages to a user.

Django Forms with ReactJS

I have Django forms rendered using Django templates. Now I want to add a React component to one of the form fields (and perhaps to more than one field in the long term).
Based on what I have read so far, its seem best not to mix Django templating with React rendering and have Django serve only as backend API sending JSON data to React, while React takes over the entire form rendering. So I am now trying to re-render my forms entirely through React.
Instead of forms.py, I have now created serializers.py to define what data is to be sent to React and have Django Rest Framework setup in my environment. Now I am trying to figure out how to send this data across. There are some good online tutorials (and SO posts) that talk about integrating Django/DRF with React but havent found a single example of end-to-end form rendering through React and DRF.
Specifically, can anyone let me know what do I really write in my view that can then be useful for the GET request from React that tries to fetch the form data? A web reference or just the broad steps needed should be enough for me to get started (and to dig in more into the docs).
Update:
Also adding a simplified version of the serializers.py code here:
from .models import Entity
from rest_framework import serializers
class EntitySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Entity
fields = ['name', 'role', 'location']
First, i think you need to check related React documentation about forms with multiple inputs. It gives you base idea about how things should be structured in React side.
About fetching data from server, you can try something like this in componentDidMount:
componentDidMount() {
// Assuming you are using jQuery,
// if not, try fetch().
// Note that 2 is hardcoded, get your user id from
// URL or session or somewhere else.
$.get('/api/profile/2/', (data) => {
this.setState({
formFields: data.fields // fields is an array
});
});
}
Then you can create your html input elements in render method with something like this:
render () {
let fields = this.state.formFields.map((field) => {
return (
<input type="text" value={field.value} onChange={(newValue) => {/* update your state here with new value */ }} name={field.name}/>
)
});
return (
<div className="container">
<form action="">
{fields}
<button onClick={this.submitForm.bind(this)}>Save</button>
</form>
</div>
)
}
And here is your submitForm method:
submitForm() {
$.post('/api/profile/2/', {/* put your updated fields' data here */}, (response) => {
// check if things done successfully.
});
}
Update:
Here is an untested-but-should-work example for your DRF view:
from rest_framework.decorators import api_view
from django.http import JsonResponse
from rest_framework.views import APIView
class ProfileFormView(APIView):
# Assume you have a model named UserProfile
# And a serializer for that model named UserSerializer
# This is the view to let users update their profile info.
# Like E-Mail, Birth Date etc.
def get_object(self, pk):
try:
return UserProfile.objects.get(pk=pk)
except:
return None
# this method will be called when your request is GET
# we will use this to fetch field names and values while creating our form on React side
def get(self, request, pk, format=None):
user = self.get_object(pk)
if not user:
return JsonResponse({'status': 0, 'message': 'User with this id not found'})
# You have a serializer that you specified which fields should be available in fo
serializer = UserSerializer(user)
# And here we send it those fields to our react component as json
# Check this json data on React side, parse it, render it as form.
return JsonResponse(serializer.data, safe=False)
# this method will be used when user try to update or save their profile
# for POST requests.
def post(self, request, pk, format=None):
try:
user = self.get_object(pk)
except:
return JsonResponse({'status': 0, 'message': 'User with this id not found'})
e_mail = request.data.get("email", None)
birth_date = request.data.get('birth_date', None)
job = request.data.get('job', None)
user.email = e_mail
user.birth_date = birth_date
user.job = job
try:
user.save()
return JsonResponse({'status': 1, 'message': 'Your profile updated successfully!'})
except:
return JsonResponse({'status': 0, 'message': 'There was something wrong while updating your profile.'})
And this is related url for this view:
urlpatterns = [
url(r'^api/profile/(?P<pk>[0-9]+)/$', ProfileFormView.as_view()),
]

Passing view variables directly to dajaxice?

I was wondering if there is a way to pass view variables directly to dajaxice?
Actually I convert the id to a json string and load them via the dajaxice function.
I like to avoid passing the id this way and have to handle it as user manipulated input.
view.py
from django.shortcuts import render_to_response, redirect
from django.shortcuts import get_object_or_404
from django.template import RequestContext
from django.utils import simplejson
def site(request, slug, number):
collection = get_object_or_404(Collection, slug=slug)
site = get_object_or_404(Site, collection=collection, number=number)
json_id = simplejson.dumps(site.pk)
return render_to_response('base/site.html',
{
'site': site,
'json_id': json_id,
},
context_instance=RequestContext(request))
site.html
...
var id = {{ json_id|safe }};
var user_changes = "";
...
Dajaxice.base.submit_changes(callback, {'id': id, 'user_changes': user_changes});
ajax.py
from django.utils import simplejson
from dajaxice.decorators import dajaxice_register
#dajaxice_register
def submit_changes(request, id, user_changes):
...
return simplejson.dumps({'message':'You changed:%s in Site #%s!' % (user_changes, id)})
The best way is probably to store such information in the session to prevent manipulation
view.py
...
request.session['id'] = site.pk
...
ajax.py
...
id = request.session['id']
...