Combining Optional Passed query filters in Peewee - python-3.x

I am trying to Link a flask server to a Peewee database. I have a Rest GET request that passes data of the form
{'a':1,'b':2, 'filter':{'name':'Foo', 'count':3}}
I want to write a method that converts my filters into a database query and execute it to return their resource:
import datetime
import peewee as pw
import uuid
DATABASE = "Resources.db"
database = pw.SqliteDatabase(DATABASE)
class BaseModel(pw.Model):
class Meta:
database = database
class Resource(BaseModel):
name = pw.CharField(unique=True)
current_count = pw.IntegerField(default=1)
total_count = pw.IntegerField(default=1)
flavor = pw.CharField(default="undefined")
users = pw.TextField()
metadata = pw.TextField(default="")
is_avalible = pw.BooleanField(default=True)
uuid = pw.UUIDField(primary_key=True, default=uuid.uuid4)
max_reservation_time = pw.IntegerField(default=10)
def __str__(self):
return f"My name is {self.name} {vars(self)}"
This is kinda what my resource looks like. Here is what I am trying to do... (not a working full example)
def filter(filters):
for i,j in filters.items():
dq = Resource.select().where(getattr(Resource, i) == j)
for resource in dq:
print(resource)
if __name__ == "__main__":
try:
database.connect()
except pw.OperationalError:
print("Open Connection")
try:
create_tables()
except pw.OperationalError:
print("Resource table already exists!")
with database.atomic():
reso = Resource.create(name="Burns", current_count=4, total_count=5, users="Bar", blah=2)
filter({'name':"Burns","total_count":5})
Here I would expect to get back: My name is Burns {'__data__': {'uuid': UUID('80808e3a-4b10-47a5-9d4f-ff9ff9ca6f5c'), 'name': 'Burns', 'current_count': 4, 'total_count': 5, 'flavor': 'undefined', 'users': 'Grant', 'metadata': '', 'is_avalible': True, 'max_reservation_time': 10}, '_dirty': set(), '__rel__': {}}I believe I might be able to create individual peewee.expressions and join them some how, I just am not sure how.

Since peewee expressions can be arbitrarily combined using the builtin & and | operators, we'll use the reduce() function to combine the list using the given operand:
def filter(filters):
expression_list = [getattr(Resource, field) == value
for field, value in filters.items()]
# To combine all expressions with "AND":
anded_expr = reduce(operator.and_, expression_list)
# To combine all expressions with "OR":
ored_expr = reduce(operator.or_, expression_list)
# Then:
return Resource.select().where(anded_expr) # or, ored_expr

Thanks to #coleifer for the reminder. Here was my solution:
OP_MAP = {
"==": pw.OP.EQ,
"!=": pw.OP.NE,
">": pw.OP.GT,
"<": pw.OP.LT,
">=": pw.OP.GTE,
"<=": pw.OP.LTE,
}
def _generate_expressions(model, query_filter):
expressions = []
for expression in query_filter:
expressions.append(
pw.Expression(
getattr(model, expression["attr"]), OP_MAP[expression["op"]], expression["value"]
)
)
return expressions
def generate_query(model, query_data):
if query_data.get("filters") is None:
database_query = model.select()
else:
database_query = model.select().where(
*(_generate_expressions(model, query_data["filters"]))
)
return database_query
I pass the type of object I want to create an expression for and operator in the filter data. Iterating over the filters I can build the expressions and combine them.

Related

Python MongoDB Motor dynamically create Indexes

I am attempting to write a function which will async create Indexes for each of my collections dynamically. The struggle I am having is accessing the class instance of each of my collections, and then calling the object to create the index.
I have tried:
getattr
ast.literal_eval
NOTE: I am unable to use eval() for security reasons
Example of how my class instance looks:
import ast
from models.database_models import IndexedKeyModels
class Mongo: # pylint: disable=too-many-instance-attributes
def __init__(self):
self.connection = self.get_set_connection() # Function to create MongoDB connection
self.db = self.connection.pal_db
self.test1 = self.db.test1
self.test2 = self.db.test2
async def set_indexes(self) -> None:
test_string: str = ""
if self.dev == "True":
test_string += "test_"
for name in IndexedKeyModels():
await ast.literal_eval(
f"self.{test_string}{name[0]}.create_index([('{name[1]}', {ASCENDING})], background=True)"
)
The IndexedKeyModels looks like this:
class IndexedKeyModels(BaseModel):
"""
MongoDB Indexed Key names
"""
test1: str = "Index1"
test2: str = "Index2"
I would like to be able to dynamically create each Index for each collection without having to define each create_index for each collection we have.
If you want to create an index on a specific collection and field, you can do this programatically with this construct:
collectionname = 'coll1'
fieldname = 'field1'
db[collectionname].create_index([(fieldname , pymongo.ASCENDING)])

How to overwrite Django Admin result_list?

Django newbie here..
I am working on a project that I need to do search in Django Admin Panel.
I have a model named SearchModel and it has colums named "id", "word" and an object called Desert and this object has '1' as id
I have another model named Result000 ('000' refers to the first three letters of md5 of "Desert" word). It has columns named "id", "word_id", "score", "title" and has an object named "Sahara Desert" whose word_id is the same as the id of the Desert object in the first table.
No ForeignKey or any other relation types between those table's items
here's the question:
When I search for Desert in SearchModel's search field. I want to list all objects in table Result000 which have word_id same as id of Desert object in SearchModel
here's my current code:
# root/admin.py
class BookAdmin(admin.ModelAdmin):
def __init__(self, model, admin_site):
self.list_display = [field.name for field in model._meta.fields]
self.search_fields = [field.name for field in model._meta.fields]
self.temp_model = "SearchModel"
self.temp_term = ""
self.word_id = None
self.search_term = self.temp_term
super().__init__(model, admin_site)
def get_search_results(self, request, queryset, search_term):
queryset, may_have_duplicate = super().get_search_results(
request, queryset, search_term
)
if len(search_term) > 0: self.search_term = search_term
else: self.search_term = self.temp_term
hashable, ret = self.md5_er(LowerTurkish(self.search_term).lower())
if hashable: table_name = f"Result{ret}"
else: table_name = request.path_info.split("/")[-2]
try:
# modelname = apps.get_model("root", table_name)
modelname = self.model_finder(table_name)
print("try içinde modelname: ", modelname)
messages.info(
request,
f"`{table_name}` modeli içindeki arama sonuçları")
word_id = self.model_finder(self.temp_model).objects.values_list("id")
std_content = modelname.objects.filter(word_id=word_id)
queryset = std_content
except:
std_content = self.model_finder(self.temp_model).objects.all()
# "nonetype not iterable" hatasının çözümü
return queryset, may_have_duplicate
def model_finder(self, table_name):
return apps.get_model("root", table_name)
def md5_er(self, data):
"""
:#params: data: md5 hashi alınacak olan veri, type: string
"""
hash_object = hashlib.md5(data.encode())
md5_hash = hash_object.hexdigest()
if md5_hash != "d41d8cd98f00b204e9800998ecf8427e":
"d41d8cd98f00b204e9800998ecf8427e refers md5 of empty string"
ret = md5_hash[:3]
return True, ret
else:
return False, False
I can get what I want if the SearchModel and Desert table have the same column names, but it doesn't work when the column names are changed. I've read almost all of Django's documentation, looked up examples from the internet, and looked at almost all entries on stackoverflow but as I said I'm Django newbie 🥲. Thanks in advance

How to convert python dict to DictRow object

Hi I am writing unittest using pytest. But I am not able to mock few db functions. We are using psycopg2 for db connections and executions. Response of query returned from psycopg2 is of the type DictRow which can be accessed either by key or by index.
Ex:
response = ['prajwal', '23', 'engineer'] #Response of a query "select name, age , job from users"
>>>response[0]
'prajwal'
>>>response['name']
'prajwal'
I want to know is there any way by which we can covert dict/list to above mentioned type.
Looking at the source for psycopg2, creating a DictRow requires passing in a DictCursor object. However the only thing it uses from DictCursor appears to be an index and description attribute.
# found in lib\site-packages\psycopg2.extras.py
class DictRow(list):
"""A row object that allow by-column-name access to data."""
__slots__ = ('_index',)
def __init__(self, cursor):
self._index = cursor.index
self[:] = [None] * len(cursor.description)
The index looks like a dict with a mapping a key to an index. e.g.response['name'] = 0
The description looks like your dict that you want to convert.
If you're feeling hacky you could take advantage of duck typing and pretend you're passing in a cursor when you're just satisfying the requirements.
The only caveat is after we instantiate the DictRow, we need to populate it. Our fake cursor hack will take care of the rest.
from psycopg2.extras import DictRow
class DictRowHack:
def __init__(self, my_dict):
# we need to set these 2 attributes so that
# it auto populates our indexes
self.index = {key: i for i, key in enumerate(my_dict)}
self.description = my_dict
def dictrow_from_dict(my_dict):
# this is just a little helper function
# so you don't always need to go through
# the steps to recreate a DictRow
fake_cursor = DictRowHack(my_dict)
my_dictrow = DictRow(fake_cursor)
for k, v in my_dict.items():
my_dictrow[k] = v
return my_dictrow
response = {'name': 'prajwal', 'age': '23', 'job': 'engineer'}
my_dictrow = dictrow_from_dict(response)
print(my_dictrow[1])
print(my_dictrow['name'])
print(type(my_dictrow))

Iterating through class variables in python

Please correct my code
PS - i'm fairly new to python
class Contact:
def __init__(self,cid, email):
self.cid=cid
self.email=email
def ind(contacts):
index={}
#Code here
return index
contacts = [Contact(1,'a'),
Contact(2,'b'),
Contact(3,'c'),
Contact(4,'a')]
print(ind(contacts))
Need the output to be like -
{'a':[1,4], 'b':2, 'c':3}
The following methods create list values like:
{'a':[1,4], 'b':[2], 'c':[3]}
I can't imagine why this wouldn't be fine, but I've added a method at the end that gets your specific output.
This doesn't maintain order of the emails:
def ind(contracts):
index={}
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
To maintain order (e.g. start with 'a'), add from collects import OrderedDict to the top of your file and then the method is:
def ind(contracts):
index = OrderedDict()
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
The printout of index will look different, but it acts the same as a normal dict object (just with ordering).
Exact output (with ordering):
def ind(contracts):
index = OrderedDict()
for contract in contracts:
if contract.email in index:
value = index[contract.email]
if not isinstance(value, list):
index[contract.email] = [value]
index[contract.email].append(contract.cid)
else:
index[contract.email] = contract.cid
return index

Getting the correct information from differently formulated queries

Howdo people,
I'm to put together a limited Q&A program that will allow the user to query Wikidata using SPARQL with very specific/limited query structures.
I've got the program going, but I'm running into issues when entering queries that are formulated differently.
def sparql_query(line):
m = re.search('What is the (.*) of (.*)?', line)
relation = m.group(1)
entity = m.group(2)
wdparams['search'] = entity
json = requests.get(wdapi, wdparams).json()
for result in json['search']:
entity_id = result['id']
wdparams['search'] = relation
wdparams['type'] = 'property'
json = requests.get(wdapi, wdparams).json()
for result in json['search']:
relation_id = result['id']
fire_sparql(entity_id, relation_id)
return fire_sparql
As you can see, this only works with queries following that specific structure, for example "What is the color of night?" Queries along the lines of 'What are the ingredients of pizza?' simply would cause the program to crash because it doesn't follow the 'correct' structure as set in the code. As such, I would like it to be able to differentiate between different types of query structures ("What is.." and "What are.." for example) and still collect the needed information (relation/property and entity).
This setup is required, insofar as I can determine, seeing as the property and entity need to be extracted from the query in order to get the proper results from Wikidata. This is unfortunately also what I'm running into problems with; I can't seem to use 'if' or 'while-or' statements without the code returning all sorts of issues.
So the question being: How can I make the code accept differently formulated queries whilst still retrieving the needed information from them?
Many thanks in advance.
The entirety of the code in case required:
#!/usr/bin/python3
import sys
import requests
import re
def main():
example_queries()
for line in sys.stdin:
line = line.rstrip()
answer = sparql_query(line)
print(answer)
def example_queries():
print("Example query?\n\n Ask your question.\n")
wdapi = 'https://www.wikidata.org/w/api.php'
wdparams = {'action': 'wbsearchentities', 'language': 'en', 'format': 'json'}
def sparql_query(line):
m = re.search('What is the (.*) of (.*)', line)
relation = m.group(1)
entity = m.group(2)
wdparams['search'] = entity
json = requests.get(wdapi, wdparams).json()
for result in json['search']:
entity_id = result['id']
wdparams['search'] = relation
wdparams['type'] = 'property'
json = requests.get(wdapi, wdparams).json()
for result in json['search']:
relation_id = result['id']
fire_sparql(entity_id, relation_id)
return fire_sparql
url = 'https://query.wikidata.org/sparql'
def fire_sparql(ent, rel):
query = 'SELECT * WHERE { wd:' + ent + ' wdt:' + rel + ' ?answer.}'
print(query)
data = requests.get(url, params={'query': query, 'format': 'json'}).json()
for item in data['results']['bindings']:
for key in item:
if item[key]['type'] == 'literal':
print('{} {}'.format(key, item[key]['value']))
else:
print('{} {}'.format(key, item[key]))
if __name__ == "__main__":
main()

Resources