How can I send React variable to Django view? - python

I'm using Django and React for a project that consumes Trello's API. I created functions in django views.py that will get user's boards, lists and cards. The problem is, to get a list, I need that the user select a board, because the list's endpoint requires a board id (and so on). I can show the user's boards on my react page and storage the board's id on a variable in Index.js (on pages folder), but I have no ideia how I can send the selected board id to views.py to consume Trello's api according to the user's selected board. Here's my code.
Django views.py:
from rest_framework.decorators import api_view
from rest_framework.response import Response
from dotenv import load_dotenv
from treegia.settings import TRELLO_URL
import os
import requests
load_dotenv()
TRELLO_KEY = os.getenv('TRELLO_KEY')
TRELLO_TOKEN = os.getenv('TRELLO_TOKEN')
#api_view(['GET'])
def get_boards(request):
if request.method == 'GET':
board_endpoint = TRELLO_URL+'members/me/boards'
jsonObj = {'fields':'name,id', 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
boards = requests.get(board_endpoint, json=jsonObj).json()
return Response(boards)
#api_view(['GET'])
def get_lists(request):
if request.method == 'GET':
list_endpoint = TRELLO_URL+ 'boards/' + id_board + '/lists'
jsonObj = {'fields':'name,id', 'id':id_board, 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
lists = requests.get(list_endpoint, json=jsonObj).json()
return Response(lists)
#api_view(['GET'])
def get_cards(request):
if request.method == 'GET':
card_endpoint = TRELLO_URL+ 'lists/' + id_list + '/cards'
jsonObj = {'fields':'name,id', 'id':id_list, 'key':TRELLO_KEY, 'token':TRELLO_TOKEN}
cards = requests.get(card_endpoint, json=jsonObj).json()
return Response(cards)
React Index.js:
import React from 'react';
import Navbar from '../components/Navbar';
import '../styles/index.css'
const Index = () => {
const [boards, setBoards] = React.useState([]);
const [boardId, setBoardId] = React.useState([]);
const [lists, setLists] = React.useState([]);
const [listId, setListId] = React.useState([]);
React.useEffect(() => {
getBoards();
}, []);
React.useEffect(() => {
getLists();
}, []);
async function getBoards() {
await fetch('http://localhost:8000/boards/')
.then(resp => resp.json())
.then(data => {
console.log(data);
if(data) setBoards(data);
})
}
async function getLists() {
await fetch('http://127.0.0.1:8000/lists/')
.then(resp => resp.json())
.then(data => {
console.log(data);
if(data) setLists(data);
})
}
return (
<div id='index-page'>
<Navbar />
<div className='boards'>
<h1>Your boards</h1>
<div className='boards-container'>
{boards.map(board => (
<button key={board.id} onClick={() => {
setBoardId(board.id)
}}>{board.name}</button>
))}
</div>
</div>
<div className='lists'>
<h1>Your lists</h1>
<div className='lists-container'>
{lists.map(list => (
<button key={list.id} onClick={() => {
setListId(list.id)
}}>{list.name}</button>
))}
</div>
</div>
{boardId ? <p>{boardId}</p> : ''}
{listId ? <p>{listId}</p> : ''}
</div>
);
}
export default Index;
Basically, when the user selects a board, I what to send the id to get_lists function. Is this possible to do?

In the view
def get_lists(request):
print(request.query_params['id_list']) # 'id_list being the variable you want to pass from the front end
this sets that we are expecting a parameter from the front end get request.
Remember to add a try catch arround it..cos if that query_param doesnt exist it will throw and erroe.
Non in the FE.. while making a request, set request parameter 'id_list = '
var url = new URL('http://127.0.0.1:8000/lists')
var params = {id_list:'3'} // or whatever the params
url.search = new URLSearchParams(params).toString();
fetch(url);
Essentially you add the query params to the request url like
http://127.0.0.1:8000/lists/?id_list=3&another_param=2..
test the backend using postman or smthn before integration..to make sure the correct url and stuff.

Related

Django -> 'WSGIRequest' object has no attribute 'data' -> Error in json.loads(request.data)

I saw a similar question but the answer is rather vague. I created a function based view for updateItem. I am trying to get json data to load based on my request. but am getting the error -> object has no attribute 'data'
Views.py file:
def updateItem(request):
data = json.loads(request.data)
productId = data['productId']
action = data['action']
print("productID", productId, "action", action)
customer = request.user.customer
product = Product.objects.get(id=productId)
order, created = Order.objects.get_or_create(customer=customer,complete=False)
orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
if action == 'add':
orderItem.quantity = (orderItem.quantity + 1)
elif action == 'remove':
orderItem.quantity = (orderItem.quantity - 1)
orderItem.save()
if orderItem.quantity <= 0:
orderItem.delete()
return JsonResponse("Item was added!", safe=False)
JS File:
function updateUserOrder(productId, action) {
console.log('User is logged in...');
let url = '/update_item/';
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ productId: productId, action: action }),
})
.then((res) => {
return res.json();
})
.then((data) => {
console.log('data', data);
});
}
urls python file:
urlpatterns = [
path("",views.store,name="store"),
path("cart/",views.cart,name="cart"),
path("checkout/",views.checkout,name="checkout"),
path("update_item/",views.updateItem,name="update_item"),
]
The Error seems to also occur in my fetch function in the JS file. With my method POST. Can't find a solution, what am I doing wrong here?
The main problem is that you are trying to access 'request.data', there is no such attribute. What you want is to retrieve data from the POST request.
(Also, note that good practice is to have your views and variable names in snake_case form, whereas camelCase is used for classes):
def updateItem(request):
data = json.loads(request.POST.get('data'))
...
return JsonResponse("Item was added!", safe=False)
Although, to complete my answer, I must say that I had problems with your JS function, with the csrf token not being properly attached. My test solution:
views.py
from django.shortcuts import render
from django.http import JsonResponse
import json
def update_item(request):
return render(request, 'update_item.html', {})
def update_item_ajax(request):
data = json.loads(request.POST.get('data'))
print(data)
...
return JsonResponse({'message': '"Item was added!"'}, safe=False)
# output of update_item_ajax print
{'productId': 1, 'action': 'myaction'}
urls.py
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
path('update/item/', views.update_item, name='update-item'),
path('update/item/ajax/', views.update_item_ajax, name='update-item-ajax'),
]
update_item.html
{% extends 'base.html' %}
{% block content %}
<button onclick="updateUserOrder(1, 'action')"> update item </button>
{% endblock %}
{% block script %}
<script>
function updateUserOrder(productId, action) {
console.log('User is logged in...');
let url = "{% url 'core:update-item-ajax' %}";
var payload = {
productId: productId,
action: action
};
var data = new FormData();
data.append( 'data' , JSON.stringify( payload ) );
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
fetch(url,
{
method: 'POST',
body: data,
})
.then(function(res){ return res.json(); })
.then(function(data){ console.log(data); });
}
</script>
{% endblock %}
Try:
data = json.loads(request.body)
Because the request doesn't have data, since you pass the data as body: JSON.stringify({ productId: productId, action: action }),

React Component not Refreshing Django REST

everyone !
I'm learning React by myself, and I'm stuck here where I'm doing a REST API for a Chat APP, when I POST a new message, the component don't refresh by itself, I have to refresh the page.
I managed to refresh it putting the idmessage and vmessage in the useEffect array, but it kept hitting the API, and I'm pretty sure this wasn't supposed to happen.
There may be a lot of wrong code here and a lot of different ways to do a better project, so I'm sorry if it's bad written.
P.S: Everything is mocked for the first and second contact
My MessagePage:
const MessagesPage = () => {
let [idmessage, setIdmessage] = useState([])
let [vmessage, setVmessage] = useState([])
useEffect(() => {
getIdMessage()
getVMessage()
}, [])
let url = 'http://127.0.0.1:8000'
let getIdMessage = async () => {
let response = await fetch(`${url}/api/messages/1/2/`)
let data = await response.json()
setIdmessage(data)
}
let getIdName = (idmessage) => {
return idmessage.map((m) => (
m.contact.name
))
}
let getVName = (vmessage) => {
return vmessage.map((m) => (
m.contact.name
))
}
let getVMessage = async () => {
let response = await fetch(`${url}/api/messages/2/1/`)
let data = await response.json()
setVmessage(data)
}
const messages = idmessage.concat(vmessage).sort((a,b) => {
return a.time.localeCompare(b.time);
}) // This is the way I found to make it like a real chat and not two different groups in a chat
return (
<>
<MessageHeader/>
<div className="card bg-dark card-body messages">
{messages.map((m, index) => (
m.contact.name === getIdName(idmessage)[0] ?
<MessageDetail messages={m} key={index} c={1}/>
: m.contact.name === getVName(vmessage)[0] ?
<MessageDetail messages={m} key={index} c={2}/>
: null
))
}
<SendMessage/>
</div>
</>
)
}
export default MessagesPage
Then my SendMessage Component:
const SendMessage = () => {
let [text, setText] = useState('')
let url = 'http://127.0.0.1:8000'
const handleChange = (e) =>{
setText(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault()
sendText()
setText('')
}
let sendText = async () => {
fetch(`${url}/api/messages/1/2/`, {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"text": (text),
"contact": 1,
"other_contact": 2,
"state": false
})
})
}
return (
<div className="w-100 card-footer border bg-light textWrite">
<form className='m-0 p-0' onSubmit={handleSubmit} method="POST" autoComplete='off'>
<div className="row m-0 p-0">
<div className="col-11 m-0 p-1">
<input id="text" onChange={handleChange} className="mw-100 border rounded form-control" type="text" name="text" title="Send a Message" placeholder="Send a Message..." value={text} required/>
</div>
<div className="col-1 m-0 p-1">
<button id='sendText' className="btn btn-outline-secondary rounded border w-100" type='submit'><BsChatDots/></button>
</div>
</div>
</form>
</div>
)
}
export default SendMessage
The Serializer:
#api_view(['GET', 'POST'])
def detailMessages(request, pk, o_pk):
message = Message.objects.filter(contact=pk, other_contact=o_pk)
serializer = MessageSerial(message, many=True)
if request.method == 'GET':
return Response(serializer.data)
if request.method == 'POST':
data = request.data
contact = Contact.objects.get(id=data['contact'])
other_contact = Contact.objects.get(id=data['other_contact'])
message = Message.objects.create(
contact=contact,
other_contact=other_contact,
text=data['text'],
state=data['state']
)
serializer = MessageSerial(message, many=False)
return Response(serializer.data)
If you want to make real time messages system without hitting the database each time, you should read about WebSocket in a simple word it is opening pool connection between multiple devices to send and receive data without closing the connection after send for example like http request and this data will always pass throw this connection(channel) without reloading or database hitting, you should read more about it to better understanding.
And if you want to use it in your project you can search about: react websocket and Django Channels
Best of luck

How to connect Flask API endpoints to React app

I have an API made on Flask that has several endpoints. I am trying to use data from these end points to display a chart on my front end that shows the data to the user.
The following are my exact requirements:
Implement one or more types of charts that can be used to effectively visualize data supplied from the API endpoints. Users should be able to pick different metrics to visualize and compare with others.
My Flask API:
import os
from flask import Flask, jsonify, session, request
import sqlalchemy
import time
from functools import wraps
# web app
app = Flask(__name__)
app.secret_key = 'super_secure_key_that_should_be_in_.env'
# database engine
engine = sqlalchemy.create_engine(os.getenv('SQL_URI'))
def rate_limit(**limit_kwargs):
def decorator(function):
#wraps(function)
def wrapper(*args, **kwargs):
limit = limit_kwargs['limit'] if 'limit' in limit_kwargs else 5
seconds = limit_kwargs['window'] if 'window' in limit_kwargs else 60
session_key = 'rate_limit_' + str(request.url_rule)
if session_key not in session:
session[session_key] = []
window: list = session[session_key]
if len(window) < limit:
window.append(int(time.time()))
session[session_key] = window
return function(*args, **kwargs)
if time.time() - window[0] < seconds:
return jsonify(error='Rate limit exceeded'), 429
window.pop(0)
window.append(int(time.time()))
session[session_key] = window
return function(*args, **kwargs)
return wrapper
return decorator
#app.route('/')
#rate_limit()
def index():
return 'Welcome 😎'
#app.route('/events/hourly')
#rate_limit(limit=3, window=20)
def events_hourly():
return queryHelper('''
SELECT date, hour, events
FROM public.hourly_events
ORDER BY date, hour
LIMIT 168;
''')
#app.route('/events/daily')
#rate_limit(limit=2)
def events_daily():
return queryHelper('''
SELECT date, SUM(events) AS events
FROM public.hourly_events
GROUP BY date
ORDER BY date
LIMIT 7;
''')
#app.route('/stats/hourly')
#rate_limit(limit=3, window=20)
def stats_hourly():
return queryHelper('''
SELECT date, hour, impressions, clicks, revenue
FROM public.hourly_stats
ORDER BY date, hour
LIMIT 168;
''')
#app.route('/stats/daily')
#rate_limit(limit=2)
def stats_daily():
return queryHelper('''
SELECT date,
SUM(impressions) AS impressions,
SUM(clicks) AS clicks,
SUM(revenue) AS revenue
FROM public.hourly_stats
GROUP BY date
ORDER BY date
LIMIT 7;
''')
#app.route('/poi')
#rate_limit(limit=3)
def poi():
return queryHelper('''
SELECT *
FROM public.poi;
''')
def queryHelper(query):
with engine.connect() as conn:
result = conn.execute(query).fetchall()
return jsonify([dict(row.items()) for row in result])
My React App.js:
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import DailyEvents from "./Components/DailyEvents";
class App extends Component {
render() {
return (
<div className="App">
<header className= "App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title"> Welcome to React</h1>
</header>
<DailyEvents />
</div>
);
}
}
export default App;
My Components/DailyEvents.js:
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
state = {
dates: []
}
componentDidMount() {
axios.get('api_url_or_localhost/events/daily')
.then(res => res.json())
.then((data) => {
this.setState({ dates: data })
})
.catch(console.log)
}
}
export default App;
I keep getting a TypeError that instance.render is not a function.
What I am trying to do is use my Flask API endpoints to visualize the data on the front-end.
try using axios :
https://alligator.io/react/axios-react/
it should look like this :
axios.get('put the query address')
.then(response => {
process your response
})
Can / Should you make changes to the Flask API, or is that code part of the requirements? Because I'm pretty sure you won't be able to return those Date objects straight from the DB without serializing them first.
I suggest you use Postman (or just a browser, for get requests) to test the routes locally and basically have a look at the data first.
React can use Axios or fetch to get this data and visualize it.
import React, { Component } from 'react'
class App extends Component {
state = {
dates: []
}
componentDidMount() {
fetch('api_url_or_localhost/events/daily'))
.then(res => res.json())
.then((data) => {
this.setState({ dates: data })
})
.catch(console.log)
}
...
}
see this https://pusher.com/tutorials/consume-restful-api-react for more details

How to return function result in GraphQL

I'm using React, Apollo and GraphQL at the frontend and Django, Python and Graphene at the backend.
I want to implement search_bar and when user click on search button I want to execute GraphQL query with the user input.
At the backend I want to grap that string and I want to pass it to an function and then returned result from the function I want to pass back to the frontend.
I have code as follows:
FRONTEND
export const GET_RESULTS = gql`
query SearchVinResults($vin: String!){
searchVinResults(vin: $vin) {
id
label
url
}
}`;
class VinSearch extends Component {
onVinSearch(e) {
e.preventDefault();
const {value, client: {query}} = this.props;
query({query: GET_RESULTS, variables: { vin: value }});
}
render() {
const {value, clearSearch, onChange} = this.props;
return (
<SearchField
onChange={e => onChange(e)}
clearSearch={() => clearSearch()}
value={value}
clearIcon="fal fa-times"
placeholder="Search VIN"
withButton={true}
buttonStyles={{borderLeftWidth: 0}}
onClick={e => this.onVinSearch(e)}
/>
);
}
}
export default withApollo(VinSearch);
Backend
class Query(object):
search_vin_results = ???? # What do I need to do here
def resolve_search_vin_results(self, info, **kwargs):
# Here I want to do something like
vin = info['vin']
results = get_vins(vin)
return results
Any idea?

Pagination with appengine ndb Cursor - python : Same cursor is being generated leading to repeatition of output results

I realised from my appengine log that the same cursor is being generated each time, the call to the Homehandler is made.
Please any idea what i am doing wrong, below is a snippet of my code:
class HomeHandler(webapp2.RequestHandler):
def get(self):
#page=self.request.get("page", default_value="1");
q = Allnews.query().order(-Allnews.date_added)
cursor = ndb.Cursor(urlsafe=self.request.get('cursor',default_value=None))
items, next_curs, more = q.fetch_page(30, start_cursor=cursor)
if more:
next_c = next_curs.urlsafe()
else:
next_c = None
context = { "news":items,"cursor":next_c}
# context["headlines"]=Feed.query(Feed.feed_cat.title == 'Headlines')
# context["gossip"] = Feed.query(Feed.feed_cat.title == 'Gossip')
# context["sports"] = Feed.query(Feed.feed_cat.title == 'Sports')
self.response.out.write(template.render('templates/homefeed.html',context))
this is the section of my homefeed.html template, I m using 'infinite scrolling' technique to fetch more results
<script>
{% if cursor %}
$(window).scroll(function()
{
var src=$("#src_val").val();
if($(window).scrollTop() == $(document).height() - $(window).height())
{
$('div#loadmoreajaxloader').show();
$.ajax({
url:"/home",
type:'GET',
data: {cursor: '{{cursor}}',feed_id:src },
success: function(news)
{
if(news)
{
$("#wrapper").append(news);
$('div#loadmoreajaxloader').hide();
}else
{
$('div#loadmoreajaxloader').html('No more posts to show.');
}
}
});
}
});
{% endif %}
</script>
It looks like you're using the get() method for both displaying a page and to handle an AJAX request. It's correctly generating a page with an initial cursor, but your $.ajax() method is expecting it to return JSON data.
Split the page request and AJAX request into two methods. Try adding a post() method to your HomeHandler that returns JSON data like this:
import json
def post(self):
q = Allnews.query().order(-Allnews.date_added)
cursor = ndb.Cursor(urlsafe=self.request.get('cursor',default_value=None))
items, next_curs, more = q.fetch_page(30, start_cursor=cursor)
if more:
next_c = next_curs.urlsafe()
else:
next_c = None
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(json.dumps({'news': items, 'cursor': next_c))
Now you have a method that returns JSON data to the AJAX request:
<script>
var cursor = null;
$(window).scroll(function()
{
if($(window).scrollTop() == $(document).height() - $(window).height())
{
$.ajax({
url:"/home",
type:'POST',
data: {cursor: cursor},
success: function(data)
{
$("#wrapper").append(data['news']);
cursor = data['cursor'];
if ( !cursor )
$('div#loadmoreajaxloader').show().html('No more posts to show.');
}
});
}
});
</script>
Notice how the cursor value is updated on each AJAX request. This is how you will get a new cursor on the next request.
(This code was not tested, you may need to debug it.)

Categories

Resources