Access nested property in pymongo - python

I'm trying to access "msgId", which is nested. However, it returns the property name instead of the value. What am I doing wrong?
Code:
def get_captain_message_id(game_id, user):
data = {
"gameId":game_id,
"messageIds":str(user.id)
}
return mongo.db['GameData'].find_one(data)['msgId']

That's because you're trying to access a property of the inner document as if it belonged to the outer one.
First you have to change your query data to match the document whose messageIds object contains the user.id property.
data = {
"gameId":game_id,
f"messageIds.{user.id}": {"$exists": True}
}
Then you can access msgId value with the appropriate indexing pipeline:
return mongo.db['GameData'].find_one(data)['messageIds'][str(user.id)]['msgId']

Related

How to access a specific dictionary value present inside a function in python

I want to take the key value of location_data['country'] and compare it with a string value(name of a country) outside the function.
import requests
def get_ip():
response = requests.get(
'https://api64.ipify.org?format=json').json()
return response["ip"]
def get_location():
ip_address = get_ip()
response = requests.get(
f'https://ipapi.co/{ip_address}/json/').json()
location_data = {
"ip": ip_address,
"city": response.get("city"),
"region": response.get("region"),
"country": response.get("country_name")
}
return location_data
Just compare it after you call the get_location function.
location_data = get_location()
if location_data.get('country') == 'country_string':
pass
Try using get method when looking for a key's value in dict, otherwise, you might have a KeyError if the key doesn't exist.
Wouldn't it be get_location()['country'] == 'string value' ?
It seems that you may be unfamiliar with how return works.
Since you are returning the dictionary, you can store it in a variable and access it.
location_data = get_location()
if location_data['country'] == "whateverstring":
#Do whatever here
Basically, you are storing what the function returns in a variable, which, in this case is the dictionary of the location data, which you can access outside of the function.
Ramintafromlt also provides a way of doing it, instead of storing what the function returns in a variable, you can just directly compare the value for that particular key.

Getting back property of object in a query list SQLAlchemy

I have a join table called UserServices. Which has a FK of service_id and makes a back ref to a service. Below I get all the userServices in which the id in the route param matches the user_id (another FK)
I am then trying to access all the service properties on the all_user_services list.
My current code only returns one dict instead of a list of dicts. What am i doing wrong?
#bp.route('/user/<id>/services', methods=['GET'])
def get_services_from_user(id):
all_user_services = db_session.query(UserService).filter(UserService.user_id == id).all()
for service in all_user_services:
result = service_schema.dump(service.service)
return jsonify(result)
You just return on first for iteration. You need to create result list:
dumped = [service_schema.dump(s.service) for s in all_user_services]
return jsonify(dumped)

Filter elements by optional lists of related elements

I have a feeling that I've made things more complex than they need to be - this can't possibly be such a rare case. It seems to me that it should be possible - or perhaps that I'm doing something fundamentally wrong.
The issue at hand is this: I've declared a database element, Element, which consists of about 10 many-to-many relations with other elements, one of which is Tag.
I want to enable the user of my application to filter Element by all of these relations, some of them or none of them. Say the user wants to see only Elements which are related to a certain Tag.
To make things even more difficult, the function that will carry out this objective is called from a graphql API, meaning it will recieve ID's instead of ORM objects.
I'm trying to build a resolver in my Python Flask project, using SQLAlchemy, which will provide an interface like so:
# graphql request
query getElements {
getElements(tags:[2, 3] people:[8, 13]) {
id
}
}
# graphql response
{
"data": {
"getElements": [
{
"id": "2"
},
{
"id": "3"
},
{
"id": "8"
}
]
}
}
I imagine the resolver would look something like this simplified pseudo-code, but I can't for the life of me figure out how to pull it off:
def get_elements(tags=None, people=None):
args = {'tags' : tags, 'people' : people}
if any(args):
data_elements = DataElement.query.filter_by(this in args) # this is the tricky bit - for each of DataElements related elements, I want to check if its ID is given in the corresponding argument
else:
data_elements = DataElement.query.all()
return data_elements
Here's a peek at the simplified database model, as requested. DataElement holds a lot of relations like this, and it works perfectly:
class DataElement(db.Model):
__tablename__ = 'DataElement'
id = db.Column(db.Integer, primary_key=True)
tags = db.relationship('Tag', secondary=DataElementTag, back_populates='data_elements')
class Tag(db.Model):
__tablename__ = 'Tag'
id = db.Column(db.Integer, primary_key=True)
data_elements = db.relationship('DataElement', secondary=DataElementTag, back_populates='tags')
DataElementTag = db.Table('DataElementTag',
db.Column('id', db.Integer, primary_key=True),
db.Column('data_element_id', db.Integer, db.ForeignKey('DataElement.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('Tag.id'))
)
Please, ORM wizards and python freaks, I call upon thee!
I've solved it in a rather clunky manner. I suppose there must be a more elegant way to pull this off, and am still holding out for better answers.
I ended up looping over all the given arguments and using eval() (not on user input, don't worry) to get the corresponding database model. From there, I was able to grab the DataElement object with the many-to-many relationship. My final solutions looks like this:
args = {
'status' : status,
'person' : people,
'tag' : tags,
'event' : events,
'location' : locations,
'group' : groups,
'year' : year
} # dictionary for args for easier data handling
if any(args.values()):
final = [] # will contain elements matching criteria
for key, value in args.items():
if value:
model = eval(key.capitalize()) # get ORM model from dictionary key name (eval used on hardcoded string, hence safe)
for id in value:
filter_element = model.query.filter_by(id=id).one_or_none() # get the element in question from db
if filter_element:
elements = filter_element.data_elements # get data_elements linked to element in question
for element in elements:
if not element in final: # to avoid duplicates
final.append(element)
return final

How do I assign a oneof field on a protobuf message if the child message has no fields?

I want to create a BigTable DeleteFromRow mutation. The proto for the Mutation and the DeleteFromRow look like this:
oneof mutation {
// Set a cell's value.
SetCell set_cell = 1;
// Deletes cells from a column.
DeleteFromColumn delete_from_column = 2;
// Deletes cells from a column family.
DeleteFromFamily delete_from_family = 3;
// Deletes cells from the entire row.
DeleteFromRow delete_from_row = 4;
}
}
message DeleteFromRow {
}
In Python, you cannot directly instantiate a DeleteFromRow object and set the delete_from_row field of the Mutation to that object.
So this does not work:
request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
mutation = request.mutations.add()
mutation.delete_from_row = data_pb2.Mutation.DeleteFromRow()
As raised by other SO users (see this question), that results in a
AttributeError: Assignment not allowed to composite field "delete_from_row" in protocol message object.
According to the protobuf docs, you should set a oneof field by setting one of the child fields. So a DeleteFromFamily mutation should be created this way:
mutation.delete_from_family.family_name = 'some_family'
However, how do I do that for the DeleteFromRow message that has no fields?
You can use Message.SetInParent:
Mark this as present in the parent.
This normally happens automatically when you assign a field of a sub-message, but sometimes you want to make the sub-message present while keeping it empty. If you find yourself using this, you may want to reconsider your design.
Example:
message Msg {
oneof kind {
int64 int_field = 1;
EmptyMsg msg_field = 1;
}
}
message EmptyMsg {}
msg = Msg()
print(msg.WhichOneof('kind')) # None
msg.msg_field # No-op (return EmptyMsg but don't set oneof field)
print(msg.WhichOneof('kind')) # None
msg.msg_field.SetInParent()
print(v.WhichOneof('kind')) # msg_field
You can initiate the DeleteFromRow object and create a mutation with the keyword argument delete_from_row:
dfr = data_pb2.Mutation.DeleteFromRow()
mutation = data_pb2.Mutation(delete_from_row=dfr)
While you cannot add or append this mutation to the repeated mutations field of a request (although it appears to me that that is what the docs says here), you can extend it:
request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
request.mutations.extend([mutation])

Flask Pymongo Objectid link not working

I am tring to access a new document from a mongo database collection named games by the _id. But for example if I access localhost:5000/solutie/5ae71f3e8e442b090e4c313bit is giving me the error: ValueError: View function did not return a response so it doesn't go through the if and I think I should convert the value of the _id to another type but I don't know how.
This is my flask code:
#app.route('/solutie/<id>')
def solu(id):
games = mongo.db.games
game_user = games.find_one({'_id' : id})
if game_user:
return id
This is my mongo database collection named games:
{
"_id": {
"$oid": "5ae71f3e8e442b090e4c313b"
},
"sursa1": "nothingfornow",
"sursa2": "nothing4now",
"corectat": 0,
"player2": "test",
"player1": "test2",
"trimis1": 1,
"trimis2": 1
}
There's an object type converter you can use for URL routing:
#app.route('/solutie/<ObjectID:game_id>')
def solu(game_id):
games = mongo.db.games
game_user = games.find_one_or_404({'_id' : game_id})
return game_user
See:
https://flask-pymongo.readthedocs.io/en/latest/#flask_pymongo.BSONObjectIdConverter
Also, don't override id() because this is an in-built Python function.
The second parameter of the find() method is an object describing which fields to include in the result.
This parameter is optional and if omitted, all fields are included in the result.
# #app.route('/solutie/<int:id>') # or
#app.route('/solutie/<string:id>')
def solu(id):
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["mydatabase"]
games = mydb["games"]
game_user = games.find({},{ "_id": id})
if game_user is not None:
return id
else:
return render_template("index.html")
Also you should use "else" condition.

Categories

Resources