How to Replicate Multidict for Flask Post Unit Test Python - python

So in my flask app, I have a form on the frontend that gets populated with several users. Each user is associated with a checkbox with the name 'selected_user'. On submit, the form is posted through standard HTML form controls (no javascript or manual ajax of any kind).
In the backend, I can parse this using
flask.request.form.getlist('selected_user')
and it returns a list of users as I expect (a user here is itself a dictionary of unique keys and associated values).
Printing out flask.request.form looks like so for example:
ImmutableMultiDict([
('_xsrf_token', u'an_xsrf_token_would_go_here'),
('selected_user', u'{u\'primaryEmail\': u\'some_value\'}'...),
('selected_user', u'{u\'primaryEmail\': u\'some_value\'}'...)])
My problem is, I cannot seem to replicate this format in my unit tests for the life of me. Obviously, I could use some javascript to bundle the checked users on the frontend into an array or whatever and then duplicate that area much easier on the backend, and that may very well be what I end up doing, but that seems like an unnecessary hassle just to make this function testable when it already behaves perfectly in my application.
Here is what I currently have tried in my test, which seems like it should be the correct answer, but it does not work:
mock_users = []
for x in range(0, len(FAKE_EMAILS_AND_NAMES)):
mock_user = {}
mock_user['primaryEmail'] = FAKE_EMAILS_AND_NAMES[x]['email']
mock_user['name'] = {}
mock_user['name']['fullName'] = FAKE_EMAILS_AND_NAMES[x]['name']
mock_users.append(mock_user)
data = {}
data['selected_user'] = mock_users
response = self.client.post(flask.url_for('add_user'), data=data,
follow_redirects=False)
This gives me an error as follows:
add_file() got an unexpected keyword argument 'primaryEmail'
I've also attempted sending these as query strings, sending data as json.dumps(data), encoding each mock_user as a tuple like this:
data = []
for x in range(0, 3):
my_tuple = ('selected_user', mock_users[x])
data.append(my_tuple)
None of these approaches have worked for various other errors. What am I missing here? Thanks ahead of time for any help! Also, sorry if there are an obvious syntax errors as I rewrote some of this for SO instead of copy pasting.

You can create a MultiDict, then make it Immutable:
from werkzeug.datastructures import MultiDict, ImmutableMultiDict
FAKE_EMAILS_AND_NAMES = [
{'email': 'a#a.com',
'name': 'a'},
{'email': 'b#b.com',
'name': 'b'},
]
data = MultiDict()
for x in range(0, len(FAKE_EMAILS_AND_NAMES)):
mock_user = {}
mock_user['primaryEmail'] = FAKE_EMAILS_AND_NAMES[x]['email']
mock_user['name'] = {}
mock_user['name']['fullName'] = FAKE_EMAILS_AND_NAMES[x]['name']
data.add('select_user', mock_user)
data = ImmutableMultiDict(data)
print data
This prints:
ImmutableMultiDict([
('select_user', {'primaryEmail': 'a#a.com', 'name': {'fullName': 'a'}}),
('select_user', {'primaryEmail': 'b#b.com', 'name': {'fullName': 'b'}})
])
EDIT:
The line data.add... should probably be data.add('selected_user', json.dumps(mock_user)) since it looks like the output you posted is a JSON encoded string.

Related

Extract fields from a JSON in Python(Django)

Hello I am new to the python world, and I am learning, I am currently developing a WebApp in Django and I am using ajax for sending requests, what happens is that in the view.py I get a JSON, from which I have not been able to extract the attributes individually to send to a SQL query, I have tried every possible way, I appreciate any help in advance.
def profesionales(request):
body_unicode = request.body.decode('utf-8')
received_json = json.loads(body_unicode)
data = JsonResponse(received_json, safe=False)
return data
Data returns the following to me
{opcion: 2, fecha_ini: "2021-02-01", fecha_fin: "2021-02-08", profesional: "168", sede: "Modulo 7", grafico: "2"}
This is the answer I get and I need to extract each of the values ​​of each key into a variable
You can interpret this as dict.
for key in received_json:
print(key,received_json[key])
# do your stuff here
but if it's always a object with same keys (fixed keys), you can access directly:
key_data = received_json[key]

Update custom_fields with Python in asana API

I've been trying update custom_fields per the latest version of Asana's API, very similarly to this post but with a later version of the API (e.g. I need to use update_task method). I can update fields at the top level of a task, but the custom_fields object is proving much more challenging to update. For example, I have many custom fields, and am trying to update a test field called "Update" and just set the text_value to "Hello"...
import asana
asanaPAT = 'myToken'
client = asana.Client.access_token(asanaPAT)
result = client.tasks.get_tasks({'project': 'myProjectID'}, opt_pretty=True)#, iterator_type=None)
for index, result in enumerate(result):
complete_task = client.tasks.find_by_id(result["gid"])
task_name = complete_task['name']
task_id = complete_task['gid']
custom_fields = complete_task['custom_fields']
#I can easily update top-level fields like 'name' and 'completed'...
#result = client.tasks.update_task(task_id, {'name': task_name + '(new)'}, opt_pretty=True)
#result = client.tasks.update_task(task_id, {'completed': False}, opt_pretty=True)
for custom_fieldsRow in custom_fields:
if custom_fieldsRow['name'] == "Updated":
#custom_fieldsRow['text_value'] = 'Hello'
#finished loop through individual custom fields, so update on the level of the task...
#client.tasks.update_task(task_id, {custom_fields}, opt_pretty=True)
manualCustomField = {'data': { 'custom_fields': {'gid': 'theGIDOfCustomField', 'text_value': 'Hello'} }}
resultFromUpdate = client.tasks.update_task(task_id, manualCustomField, opt_pretty=True)
As you can see above, I started off trying to loop through the custom_fields and make changes to the specific field before updating later. But now I'm even trying to manually set the custom_field data (last line of my code), but it does nothing (no error, but doesn't change my task). I'm completely out of ideas to troubleshoot this so appreciate any feedback on where I'm going wrong.
Apologies, I figured out my mistake, I just needed my penultimate line to read...
manualCustomField = { 'custom_fields': {'theGIDOfCustomField':'Hello'} }
Kinda a strange way to do that in the API (not specifically stating which field you'll update or which id you're using) if you ask me, but now it finally works.

Passing objects through Flask URL's

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

Django QueryDict variable, passed via application-form-post, how to get variables?

I trying to get some variables from one server, this server isn't mine, and I receive the POST like these:
<QueryDict: {"'data[id]': ['83A0C50B5A0A43AD8F60C1066B16A163'], 'data[status]': ['paid'], 'event': ['invoice.status_changed']": ['']}>
Here is the code:
def get_iugu_retorno(request):
d1 = request.POST
d2 = d1.get['data[id]']
I need to get data[id], data[status] and event... but Django appears to get all these information like a flattern string instead a Dict.
How is the best way to solve these?
I also try to create a list:
d2 = d1.getlist('data')
and nothing...
I`m using Django 1.8
This isn't form-encoded data at all, so you can't access it as if it is. It appears to be a form of JSON. So you need to access the post body directly.
d1 = json.loads(request.body)
d2 = d1.get('data[id]')
You are right in the assumption that data[id] is the name of the key, and not a list, but you are simply accessing the QueryDict the wrong way (get[], it's get()). The following code works fine:
from django.http import QueryDict
qd = QueryDict('data[id]=83A0C50B5A0A43AD8F60C1066B16A163&data[status]=paid&event=invoice.status_changed=')
qd.get('data[id]')
>>> u'83A0C50B5A0A43AD8F60C1066B16A163'

Pyramid route matching and query parameters

I have a Pyramid web service, and code samples are as follows:
View declaration:
#view_config(route_name="services/Prices/GetByTicker/")
def GET(request):
ticker = request.GET('ticker')
startDate = request.GET('startDate')
endDate = request.GET('endDate')
period = request.GET('period')
Routing:
config.add_route('services/Prices/GetByTicker/', 'services/Prices/GetByTicker/{ticker}/{startDate}/{endDate}/{period}')
Now I know this is all screwed up but I don't know what the convention is for Pyramid. At the moment this works inasmuch as the request gets routed to the view successfully, but then I get a "Dictionary object not callable" exception.
The URL looks horrible:
#root/services/Prices/GetByTicker/ticker=APPL/startDate=19981212/endDate=20121231/period=d
Ideally I would like to be able to use a URL something like:
#root/services/Prices/GetByTicker/?ticker=APPL&startDate=19981212&endDate=20121231&period=d
Any Pyramid bods out there willing to take five minutes to explain what I'm doing wrong?
from you sample code, i think you use the URL Dispatch
so it should be like this
config.add_route('services/Prices/GetByTicker/', 'services/Prices/GetByTicker/')
then the URL like:
#root/services/Prices/GetByTicker/?ticker=APPL&startDate=19981212&endDate=20121231&period=d
will match it
--edit--
you don't have to use a name like "services/Prices/GetByTicker" for route_name,and you can get the GET params use request.params['key']
View declaration:
#view_config(route_name="services_Prices_GetByTicker")
def services_Prices_GetByTicker(request):
ticker = request.params['ticker']
startDate = request.params['startDate']
endDate = request.params['endDate']
period = request.params['period']
Routing:
config.add_route('services_Prices_GetByTicker', 'services/Prices/GetByTicker/')
The query string is turned into the request.GET dictionary. You are using parenthesis to call the dictionary instead of accessing items via the brackets. For a url such as
#root/services/Prices/GetByTicker/?ticker=APPL&startDate=19981212&endDate=20121231&period=d
request.GET['ticker'] # -> 'APPL' or an exception if not available
request.GET.get('ticker') # -> 'APPL' or None if not available
request.GET.get('ticker', 'foo') # -> 'APPL' or 'foo' if not available
request.GET.getall('ticker') # -> ['APPL'] or [] if not available
The last option is useful if you expect ticker to be supplied multiple times.
request.params is a combination of request.GET and request.POST where the latter is a dictionary representing the request's body in a form upload.
Anyway, the answer is that request.GET('ticker') syntactically is not one of the options I mentioned, stop doing it. :-)

Categories