I've built a Django REST API using the Django Restapiframework, and it uses a serializer to return a json response:
from rest_framework import serializers
from .models import WeatherStatistics
class WeatherStatisticsSerializer(serializers.ModelSerializer):
class Meta:
model = WeatherStatistics
fields = '__all__'
In my Angular 4 front-end project I have a service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class WeatherstatsService {
constructor(private http:Http) {
console.log("Hello World");
this.getWeatherData();
// this.getWeatherDataRecords();
}
weatherdata: any = {};
private _WeatherStatsUrl = 'http://127.0.0.1:8000/weatherstats/weatherdata';
getWeatherData() {
return this.http.get(this._WeatherStatsUrl)
.map((res:Response) => res.json);
}
getWeatherDataRecords() {
this.getWeatherData().subscribe(weatherdata => {
console.log(weatherdata)
})
}
}
And I also have a component that uses that service:
import { Component, OnInit } from '#angular/core';
import { WeatherstatsService } from '../weatherstats.service';
#Component({
selector: 'weather-stats',
templateUrl: './weather-stats.component.html',
styleUrls: ['./weather-stats.component.css']
})
export class WeatherStatsComponent implements OnInit {
data: any;
constructor(private weatherstatsservice: WeatherstatsService) { }
ngOnInit() {
this.data = this.weatherstatsservice.getWeatherData();
}
}
At the moment all I want it to do is log the json data from my api to the console oninit. So far tho, it does nothing. How do I get the json from Django into Angular?
First, you haven't subscribed to your http call, and you can't set asynchronous data in synchronous manner, you need to do it like this:
this.weatherstatsservice.getWeatherData().subscribe(data => this.data = data);
Second, you have a typo, .json is a function, call it as such
.map((res:Response) => res.json());
That's because you just make the request but no subscribe to it's value when it's come back from backend. If you want to get data from your component I sugget to use
In your component.
this.weatherstatsservice.getWeatherData().subscribe(data => this.data = data);
or call this.weatherstatservice.getWeatherDataRecords() from service (but you won't be sure when it's comeback from server so you should make an observer to this or just use first method) and then grab data from service.
Related
I'm making a website where you buy stuff and pay through PayPal.
I am done with the PayPal part now I am trying to get a situation where after the payment is complete in checkout the item purchased goes to orders in the admin.
This is the order model.py:
class Order (models.Model):
product = models.ForeignKey(Coinpack, max_length=200, null=True, blank=True, on_delete = models.SET_NULL)
created = models.DateTimeField(auto_now_add=True)
this is the PayPal code part in checkout.html:
<script src="https://www.paypal.com/sdk/js?client-id=idexample¤cy=USD"></script>
<script>
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
var total = '{{checkout.price}}'
var productId = '{{checkout.id}}'
function completeOrder(){
var url = "{% url 'complete' %}"
fecth(url, {
method: 'POST',
headers:{
'content-type':'aplication/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({'productId':productId})
})
}
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: total
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For demo purposes:
completeOrder()
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
});
}
}).render('#paypal-button-container');
</script>
</body>
</html>
and here is the views.py
def paymentComplete(request):
body = json.loads(request.body)
print('BODY:', body)
product = checkout.objects.get(id=body['productId'])
Order.objects.create(
product=product
)
return JsonResponse('Payment completed!', safe=False)
So i was following a tutorial but the tutorial and mine where different so i kinda got confused and i don't know what to do, pls help.
I also have the payment complete in the urls.py:
path('complete/', views.paymentComplete, name="complete"),
Thank you.
Do not use actions.order.create() / actions.order.capture() to create and capture an order on the client side if you are then going to be doing server-side operations with the completed payment. Those JS functions are for simple use cases, not what you are trying to do.
Instead, use PayPal's v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use the (recently deprecated) Checkout-PHP-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)
I have this code on my react App.js:
import React, { Component } from 'react';
import axios from "axios";
import './App.css';
class App extends Component {
state = {
inventory: []
};
async componentDidMount() {
try {
const res = await fetch('http://localhost:8000/items/');
const inventory = await res.json();
this.setState({
inventory
});
} catch (e) {
console.log(e);
}
}
render() {
return (
<div>
{this.state.inventory.map(item => (
//<div key={item.url}>
<div className="App">
<h1>{item.name}</h1>
<span>{item.url}</span>
<span>{item.price}</span>
<span>{item.scalable}</span>
<span>{item.taxable}</span>
</div>
))}
</div>
);
}
}
export default App;
I'm using django react-framework, I want to communicate from react frontend into my django backend through the framework.
So, inventory is the name of the django app. Which is registered on my settings.py file as inventory.
It has a model of course, and one of the classes of this model, is item, which, on my django backend api, the url is defined as items. Like this router.register(r'items', ItemViewSet).
it's like a list of all the products on the database. The typical rest-framework viewset.
Also, this is all serialized, and it's working well on the backend, if I access http://localhost:8000/items/ it works, but the problem is the frontend, it just shows a blank page.
My index.js hasnt been edited, I have installed the axios library, added the "proxy": "http://localhost:8000", line on my packages.json file.
Added also the corsheaders app in my settings.py file, I also have this declared on it:
CORS_ORIGIN_WHITELIST = (
'localhost:3000/'
)
But, it is still showing just a blank page from the react frontend (App.js).
I don't know what could be the cause, if You need further code or explanation, please let me know.
I have previous experience in Django. If add line {csrf_token} in Django templates then Django handles the functionalities of csrf_token. But when I am trying to develop an API using Django REST Framework then I get stuck. How can I add and handle functionalities like csrf_token in API (back end, developed using Django REST Framework) and React Native/React JS (front end) like Django templates?
The first step is to get CSRF token which can be retrieved from the Django csrftoken cookie.
Now from the Django docs you can find out how to get the csrf token from the cookie by using this simple JavaScript function:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
Now, you can retrieve the CSRF token by calling the getCookie('csrftoken') function
var csrftoken = getCookie('csrftoken');
Next you can use this csrf token when sending a request with fetch() by assigning the retrieved token to the X-CSRFToken header.
fetch(url, {
credentials: 'include',
method: 'POST',
mode: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: {}
})
}
Rendering the CSRF Token in React Forms:
If you are using React to render forms instead of Django templates you also need to render the csrf token because the Django tag { % csrf_token % } is not available at the client side so you need to create a higher order component that retrieves the token using the getCookie() function and render it in any form.
Lets add some line in csrftoken.js file.
import React from 'react';
var csrftoken = getCookie('csrftoken');
const CSRFToken = () => {
return (
<input type="hidden" name="csrfmiddlewaretoken" value={csrftoken} />
);
};
export default CSRFToken;
Then you can simply import it and call it inside your form
import React, { Component , PropTypes} from 'react';
import CSRFToken from './csrftoken';
class aForm extends Component {
render() {
return (
<form action="/endpoint" method="post">
<CSRFToken />
<button type="submit">Send</button>
</form>
);
}
}
export default aForm;
The Django CSRF Cookie
React renders components dynamically that's why Django might not be able to set a CSRF token cookie if you are rendering your form with React. This how Django docs says about that:
If your view is not rendering a template containing the csrftoken
template tag, Django might not set the CSRF token cookie. This is
common in cases where forms are dynamically added to the page. To
address this case, Django provides a view decorator which forces
setting of the cookie: ensurecsrf_cookie().
To solve this issue Django provides the ensurecsrfcookie decorator that you need to add to your view function. For example:
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def myview(request):
I used jquery for ajax in react, so in this case here is a solution :
let csrfcookie = function() { // for django csrf protection
let cookieValue = null,
name = "csrftoken";
if (document.cookie && document.cookie !== "") {
let cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
let cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) == (name + "=")) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
$.ajax({
type: "POST",
beforeSend: function(request, settings) {
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
request.setRequestHeader("X-CSRFToken", csrfcookie());
}
},
.... /// other codes
I am using this library for datatables in django-rest. Everything is working fine expect request.user session in views. It seems to me django-datatable is not authenticating the user token and therefore request.user returns anonymous user. And the same is accessible even without sending user token in headers.
Here is my code :
class MyDataTableView(BaseDatatableView):
"""
"""
model = MyModel
columns = [***columns** ]
order_columns = [***columns**]
def get_initial_queryset(self):
"""
initial queryset for
"""
self.request.user -----> returns antonymous user
queryset = self.model.objects
return queryset
Have You tried to subclass BaseDatatableView and overwrite its .get like:
def get(self, *args, **kwargs):
super().get(*args, **kwargs)
print(self.request)
My guess is that get_initial_queryset can be invoked before actual request dispatch, so the user is anonymous there. When You look into the code of django_datatables/mixins.py, there is a mixin called JsonResponseMixin. It's GET method is directly responsible for request processing, so You should look for Your answers there. The easiest way - subclass it and overwrite the method.
Have you added the token JS to the Datatables initiation JS file? django-datatables just creates the correct JSON string. Initiating the cookie is different.
I fought with this a while and my missing piece was that I had to get and set the cookie:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
this is above where I set the Datatables params for example :
let table = $('#datatables').DataTable({
"processing": true,
"serverSide": true,
stateSave: true,
"ajax": {
........
I have this route config:
$routeProvider
.when('/store', {
templateUrl: '/static/js/partials/store.html',
controller: 'StoreController'
})
.when('/groups', {
templateUrl: '/static/js/partials/groups.html',
controller: 'GroupsController'
})
.when('/groups/:id', {
templateUrl: '/static/js/partials/group_page.html',
controller: 'GroupPageController'
})
.when('/purchases', {
templateUrl: '/static/js/partials/purchases.html',
controller: 'PurchaseController'
})
.when('/settings', {
templateUrl: '/static/js/partials/settings.html',
controller: 'SettingsController'
})
.otherwise({redirectTo: '/static/js/partials//store'});
There are lots of django variables and other django stuff used in those HTML files.
But they are not being parsed. For example:
{% trans 'vouchers' %}
is shown in the view insted of 'vouchers'
Why is this happening? How to fix this?
Django templating engine is done on the server side
Angularjs routeProvide is implemented for client side routing.
Hence, your django variables will not be parsed. (since all changes are done on client-side by angularjs and dont actually and is not interpreted by the django templating engine)