How to return function result in GraphQL - python

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?

Related

How can I send React variable to Django view?

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.

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 use Axios/Vue.js to call data from SQLite/Django backend?

Status Quo:
Whenever a user visits my web application, Axios makes a request to a third party API to fetch data and populate the frontend with that data using v-for.
Conclusion: I have one API call per website visitor.
Desired status:
Whenever a user visits my web application, Axios shall fetch the data from the SQLite database which itself is populated every XX seconds by a python request to reduce API calls.
Questions:
Now I implemented a SQLite database using Django models and views. So far so good, the API gets fetched regularly and updates the database table properly.
1.) How can I now call the data in the database using Axios? As by my research Axios somehow needs to call a view and the view will call the data from the database, is this correct?
2.) If Axios needs to call a view, do I need another view.py file that calls the database? If I would insert the needed view function into the existing view.py file it would initiate another API call, wouldn't it?
3.) And how can I implement the link to the view function to Axios? Instead of a third party API url would I just use the path to the view file?
Quotes_app/Views.py:
from django.shortcuts import render
from Quotes_app.models import ratesEUR
import json
import requests
response = requests.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
rates_EUR = json.loads(response.content.decode('utf-8'))
timestamp = rates_EUR['timestamp']
base = rates_EUR['base']
date = rates_EUR['date']
rates = rates_EUR['rates']
id = 1
rates_new = ratesEUR(id=id, timestamp=timestamp, base=base, date=date, rates=rates)
rates_new.save()
def render_Quotes_app(request, template="Quotes_app/templates/Quotes_app/Quotes_app.html"):
return render(request, template)
Quotes_app/models.py:
from django.db import models
class ratesEUR(models.Model):
timestamp = models.CharField(max_length=10)
base = models.CharField(max_length=3)
date = models.DateField(auto_now=False, auto_now_add=False)
rates = models.CharField(max_length=8)
def __str__(self):
return self.base
Vue.js/axios: (as of now directly fetching the API)
Vue.config.devtools = true;
var app = new Vue({
delimiters: ['[[', ']]'],
el: '.eurQuotesWrapper',
data() {
return {
rates: [],
};
},
computed: {
rates1() {
const ratesArr1 = Object.entries(this.rates);
const ret = ratesArr1.reduce((a, c, i, d) => {
if (i < d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates1', ret);
return ret;
},
rates2() {
const ratesArr2 = Object.entries(this.rates);
const ret = ratesArr2.reduce((a, c, i, d) => {
if (i >= d.length / 2) a[c[0]] = c[1];
return a;
}, {});
console.log('rates2', ret);
return ret;
}
},
created() {
axios
.get("http://data.fixer.io/api/latest?access_key=XXXX&base=EUR")
.then(response => {
this.rates = response.data.rates;
console.log(this.rates);
for(key in this.rates) {
this.rates[key] = new Intl.NumberFormat('de-DE', {
minimumFractionDigits: 5,
maximumFractionDigits: 5
}).format(this.rates[key]);
}
console.log(this.rates);
});
}
});
In advance thank you very much for your help!
You can use DRF(Django Rest Framework) to make REST API's or You can use JsonResponse
to send JSON objects as response.
from django.http import JsonResponse
data = # Your data
JsonResponse(data, encoder=MyJSONEncoder)
For more details visit django's documentation
Better would be if you use DRF for making rest apis. It's much easier.

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.)

Call ajax on dropdown button

I've created a form and populating its value from Category model in drop down box. Its working.
I displayed this form in template like this {{ language_form }}.It worked. Now I want to implement onchange event on this drop down with ajax. Function will call on change of drop down value.
Edit:it can be done like this without django forms . <select onchange='ajaxfunction()'></select> But I'm using django forms.
Form
from django import forms
from myapp.movie.models import Category
class Language(forms.Form):
language = forms.ModelChoiceField(queryset=Category.objects.all())
Here is my Ajax function
function showMovie(str) {
if (str == "") {
document.getElementById("txtHint").innerHTML = "";
return;
}
if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else {// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
document.getElementById("txtHint").innerHTML = xmlhttp.responseText;
}
}
xmlhttp.open("GET", "movie_list?q=" + str, true);
xmlhttp.send();
}
If you want to just add onchange attr, then:
class Language(forms.Form):
language = forms.ModelChoiceField(queryset=Category.objects.all(), widget=forms.Select(attrs={'onchange':'ajaxfunction()'}))
Or you can render form manually.
Add this code in the end of your form.
...
</form>
<script type="text/javascript">
var ddl = document.getElementById('ddlYourDropDownListID');
ddl.onchange = function() {
var str = 'someValue';
showMovie(str);
};
</script>
</body>
</html>

Categories

Resources