3

I am working on a site using Flask that is pulling data from an API, processing it into JSON, and then dynamically loading it throughout the site as needed. I am having an issue with matching the URL appropriately while at the same time efficiently loading what data I need.

Here is my main file:

import requests
from flask import Flask, render_template

app = Flask(__name__)

url = 'https://omgvamp-hearthstone-v1.p.mashape.com/cards'
myHeaders={
  "X-Mashape-Key": 'key-here',
  "Accept": "application/json"
}
r = requests.get(url, headers=myHeaders)
cards = r.json()
badSets = ['Promo', 'Hall of Fame', 'Tavern Brawl', 'Hero Skins', '    Missions', 'Credits', 'System', 'Debug']

@app.route('/')
def index():
    return render_template('index.html', cards=cards, badSets=badSets)


@app.route('/<setName>', methods=['GET'])
def set(setName):
    return render_template('set.html', cards=cards, setName=setName, badSets=badSets)


@app.route('/<setName>/<cardName>', methods=['GET'])
def individualCard(setName, cardName):
    return render_template('card.html', cards=cards, setName=setName, cardName=cardName, badSets=badSets)

First, I'd prefer to not have to pass each html page all of my data. Is there some way to pull only what is needed and ignore the rest?

Second, I would like to pass a dictionary object i.e.

if card['name'] == card[setName][--index--][cardName]:
    pass card object to html page here

How could this be achieved, and is it possible without having to use a for-loop through all of my data?

I'm happy to load my html pages if needed.

5
  • Can you give an example what your processed JSON looks like, and maybe a short description of your workflow, e.g. what are your routes used for? Maybe it is just me, but I'm having a hard time understanding what your issue is. Commented Nov 7, 2017 at 23:53
  • @bgse I appreciate the comment. The JSON is formatted such that print(cards['Basic'][0]) gives me {'cardId': 'GAME_004', 'dbfId': '1740', 'name': 'AFK', 'cardSet': 'Basic', 'type': 'Enchantment', 'text': 'Your turns are shorter.', 'playerClass': 'Neutral', 'locale': 'enUS'} where ['Basic'] is the set that I am trying to traverse while the index is pulling out an individual card from the sub-dictionary that is contained within basic. The goal of this site is to be like Mythicspoiler.com but based around Hearthstone, an online card game. Where sets and cards are displayed. Commented Nov 8, 2017 at 0:30
  • Here is the site I am pulling my API from: hearthstoneapi.com if that can help explain more about what I am trying to do... Commented Nov 8, 2017 at 0:32
  • So your application is more or less a card browser where the user can select a set of card from a list of sets, then a specific card from the selected set I take it? And you'd like to avoid passing all your json data in individualCard() to the template to look up the card there, and instead just pass a single card you extract by the parameters. Is that about correct? Commented Nov 8, 2017 at 0:41
  • Yes, that's more or less what I am attempting to do. Commented Nov 8, 2017 at 0:43

1 Answer 1

2

Assuming the basic structure of your parsed json data looks like this (a dictionary with lists of dictionaries):

{
  "Basic": [{"cardId": 4711, ...}, {"cardId": 4712, ...}, ...],
  "Fancy": [...],
  ...
}

You could rewrite:

@app.route('/<setName>', methods=['GET'])
def set(setName):
    cardset = cards.get(setName)
    return render_template('set.html', cardset=cardset)

This extracts the card set we are looking for by the dictionary key, according to the assumed data structure above.

Then in the template, instead of cardName, pass the cardId and rewrite the other route:

@app.route('/<setName>/<cardId>', methods=['GET'])
def individualCard(setName, cardId):
    cardset = cards.get(setName)
    matches = [x for x in cardset if x['cardId'] == cardId]
    card = matches[0]
    return render_template('card.html', card=card)

This uses list comprehension to extract a list of matches (everything that has the cardId we are looking for) from our selected cardset, and should be a list with a single element. We return the first element to the template for rendering.

Now this obviously does not do any error checking, for example the dictionary key we passed might be wrong and not be found, the cardId might not be found, or more interestingly there might be more than one result for the cardId we passed.

But this would be the general idea on how to approach this.

Also note I've left out badSets for clarity in the example, I'm assuming this are card sets that are forbidden, or for testing purposes.

For this case, you'd want to check the dictionary key first before looking up the set, and show an error page or something maybe:

@app.route('/<setName>', methods=['GET'])
def set(setName):
    if setName in badSets:
        return render_template('error.html')
    cardset = cards.get(setName)
    return render_template('set.html', cardset=cardset)

Disclaimer: This is coming purely from memory and it is late, so there might be an error here or there...

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the answer. I'm a bit tired myself but I'll be sure to read over this in the morning and test it out.
I'm curious as to why you used cardId rather than cardName
So, I was able to get it to work with cardName. I figure it'll be more accessible for users to enter the URL of the card they want to see. I'm curious if you could help me build the href for my template. Currently, it's {% for card in cardset %} {% if ('img') in card %} {% if ('collectible') in card %} <h1>{{ card['name'] }} <a href="{{ url_for('individualCard', setName=setName, cardName=card['name']) }}"> but somewhere along the line, I think i am losing the setName when linking away.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.