How can I create back relations with pydantic models - python

I want to make back relation structure with pydantic but I`m getting an exception.
A.update_forward_refs() doesn't help solve the problem.
Traceback (most recent call last):
File "...", line 82, in <module>
a = A(**{"x": 5, "b": {"y": 1}})
File "pydantic\main.py", line 340, in pydantic.main.BaseModel.__init__
File "pydantic\main.py", line 1077, in pydantic.main.validate_model
File "pydantic\fields.py", line 860, in pydantic.fields.ModelField.validate
pydantic.errors.ConfigError: field "b" not yet prepared so type is still a ForwardRef, you might need to call A.update_forward_refs().
What should I do?
from typing import Optional
from pydantic import BaseModel
class A(BaseModel):
x: int
b: Optional['B'] = None
class Config:
orm_mode = True
class B(BaseModel):
y: int
a: Optional['A'] = None
class Config:
orm_mode = True
a = A(**{"x": 5, "b": {"y": 1}})
print(a)

You say you tried A.update_forward_refs(), but I am guessing you put it in the wrong place because it works fine for me, when I do it. You need to call the method after the classes being referenced have been defined, in this case after the definition of B, but obviously before you actually want to use A to parse data.
from typing import Optional
from pydantic import BaseModel
class A(BaseModel):
x: int
b: Optional["B"] = None
class B(BaseModel):
y: int
a: Optional["A"] = None
A.update_forward_refs()
a = A(**{"x": 5, "b": {"y": 1}})
print(a)

Related

NameError creating instance of imported class

I have a subclassed Course class as follows:
# course.py
class Course:
"""Represent a single Course"""
kind = 'Gen Ed'
def __init__(self, name, number) :
self._name = name # 'private' instance variable\
self._number = number
self.__display()
def display(self):
print(self.kind,"Course:" , self._name, self._number, sep=" ")
__display = display # private copy
class CSCourse(Course):
"""Represent a single CS Course"""
kind = 'CS' # class variable shared by all CSCourses
def __init__(self, name, number, language, numberOfPrograms):
Course.__init__(self, name, number)
self._language = language
self._numberOfPrograms = numberOfPrograms
def display(self):
Course.display(self)
print('Language', self._language,
'Number Of programs:', self._numberOfPrograms, sep = ' ')
I import the module as follows:
>>>from course import *
This does not throw any exception, but then when I issue the following to call the constructor, I get the error below?
>>> cs360=CSCourse("Special Topics", 360, "python", 21)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'CSCourse' is not defined
What am I doing wrong please? I did also try to see what methods are available in the classes imported. It seems nothing is being imported!
>>> import inspect
>>> inspect.getmembers(Course, lambda a:not(inspect.isroutine(a)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'Course' is not defined
>>> inspect.getmembers(CSCourse, lambda a:not(inspect.isroutine(a)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'CSCourse' is not defined
For anyone else having this problem, check if you have circular imports (in file a.py from b import * and in file b.py from a import *). Python doesn't seem to raise an exception when that happens, but the import doesn't work. Restructuring the code to remove the circular import fixed the problem for me.

Creating Python Active Resource object from json file

So for testing purposes, I am trying to create a python ActiveResource object from a json file (I want the object to have attributes from the json file). More specifically I am using the ShopifyResource from (https://github.com/Shopify/shopify_python_api), which extends the ActiveResource object.
I looked through the source code and found some functions that I thought would be of some use:
(https://github.com/Shopify/shopify_python_api/blob/master/shopify/base.py)
from pyactiveresource.activeresource import ActiveResource
import shopify.mixins as mixins
class ShopifyResource(ActiveResource, mixins.Countable):
_format = formats.JSONFormat
def _load_attributes_from_response(self, response):
if response.body.strip():
self._update(self.__class__.format.decode(response.body))
where _update is from ActiveResource (https://github.com/Shopify/pyactiveresource/blob/master/pyactiveresource/activeresource.py)
def _update(self, attributes):
"""Update the object with the given attributes.
Args:
attributes: A dictionary of attributes.
Returns:
None
"""
if not isinstance(attributes, dict):
return
for key, value in six.iteritems(attributes):
if isinstance(value, dict):
klass = self._find_class_for(key)
attr = klass(value)
elif isinstance(value, list):
klass = None
attr = []
for child in value:
if isinstance(child, dict):
if klass is None:
klass = self._find_class_for_collection(key)
attr.append(klass(child))
else:
attr.append(child)
else:
attr = value
# Store the actual value in the attributes dictionary
self.attributes[key] = attr
So then I tried to do the following:
order = Order()
with open("file.json")) as json_file:
x = json.loads(json_file.read())
order._update(x)
Where Order extends ShopifyResource (which extends ActiveResource). If am not mistaken x should be a dictionary, which is an approriate parameter for the _update() function.
Yet I get the following output:
raceback (most recent call last):
File "/home/vineet/Documents/project/tests/test_sync.py", line 137, in testSaveOrder1
self.getOrder()
File "/home/vineet/Documents/project/tests/tests/test_sync.py", line 113, in getOrder
order._update(x)
File "/home/vineet/Documents/project/venv/lib/python3.6/site-packages/pyactiveresource/activeresource.py", line 962, in _update
attr.append(klass(child))
File "/home/vineet/Documents/project/venv/lib/python3.6/site-packages/shopify/base.py", line 126, in __init__
prefix_options, attributes = self.__class__._split_options(attributes)
File "/home/vineet/Documents/project/venv/lib/python3.6/site-packages/pyactiveresource/activeresource.py", line 466, in _split_options
if key in cls._prefix_parameters():
File "/home/vineet/Documents/project/venv/lib/python3.6/site-packages/pyactiveresource/activeresource.py", line 720, in _prefix_parameters
for match in template.pattern.finditer(path):
TypeError: cannot use a string pattern on a bytes-like object
I even tried the following:
order._update(order._format.decode(json_file.read()))
But that didn't work since 'str' object has no attribute 'decode'.
It seems You are worried if x has correct format. Print it, and check.
Btw: And use
x = json.load(json_file)
instead of
x = json.loads(json_file.read())

pymongo error: filter must be an instance of dict, bson.son.SON, or other type that inherits from collections.Mapping

I think the query is correct but still an error.
findQ = {"fromid": wordid}, {"toid":1}
res= self.db.wordhidden.find(findQ)
However, find_one(findQ) works. So I can't find the wrong thing.
I use python 3.6 and pymongo.
Here is my code:
def getallhiddenids(self,wordids,urlids):
l1={}
for wordid in wordids:
findQ = {"fromid": wordid}, {"toid":1}
res= self.db.wordhidden.find(findQ)
for row in res: l1[row[0]]=1
for urlid in urlids:
findQ = {"toid": urlid}, {"fromid":1}
res= self.db.hiddenurl.find(findQ)
This is an error:
Traceback (most recent call last):
File "C:\Users\green\Desktop\example.py", line 9, in <module>
neuralnet.trainquery([online], possible, notspam)
File "C:\Users\green\Desktop\nn.py", line 177, in trainquery
self.setupnetwork(wordids,urlids)
File "C:\Users\green\Desktop\nn.py", line 105, in setupnetwork
self.hiddenids=self.getallhiddenids(wordids,urlids)
File "C:\Users\green\Desktop\nn.py", line 93, in getallhiddenids
res= self.db.wordhidden.find(findQ)
File "C:\Users\green\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\pymongo\collection.py", line 1279, in find
return Cursor(self, *args, **kwargs)
File "C:\Users\green\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\pymongo\cursor.py", line 128, in __init__
validate_is_mapping("filter", spec)
File "C:\Users\green\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\pymongo\common.py", line 400, in validate_is_mapping
"collections.Mapping" % (option,))
TypeError: filter must be an instance of dict, bson.son.SON, or other type
that inherits from collections.Mapping
find_one(findQ) works
The error is because PyMongo find() requires a dictionary or a bson.son object. What you have passed in is a Python tuple object is the form of ({"fromid": wordid}, {"toid":1}). You could correct this by invoking the find() method as below:
db.wordhidden.find({"fromid": wordid}, {"toid": 1})
Technically your invocation of find_one() does not work either. It just that the parameter filter has been altered by find_one(). see find_one() L1006-1008. Which basically format your tuple filter into :
{'_id': ({"fromid": wordid}, {"toid":1}) }
The above would (should) not returned any matches in your collection.
Alternative to what you're doing, you could store the filter parameter into two variables, for example:
filterQ = {"fromid": wordid}
projectionQ = {"toid": 1}
cursor = db.wordhidden.find(filterQ, projectionQ)

Python object validation thanks to a Schema

I want to validate a python object thanks to a schema. For this I found the schema framework.
I would like to validate a numeric string:
a = {
'phone_number': '12233'
}
Do you know how can I validate this string thanks to a regex?
At this time, I only know how to perform a string validation:
Schema(str).validate('12')
Schema will call any callables; simply provide a function that uses a regular expression:
import re
pattern = re.compile('^12\d+$')
Schema(And(str, lambda x: pattern.match(x) is not None))
Demo:
>>> import re
>>> from schema import Schema, And
>>> pattern = re.compile('^12\d+$')
>>> s = Schema(And(str, lambda x: pattern.match(x) is not None))
>>> s.validate('123234')
'123234'
>>> s.validate('42')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/schema.py", line 153, in validate
raise SchemaError([None] + x.autos, [e] + x.errors)
schema.SchemaError: <lambda>('42') should evaluate to True

JSON serializing Mongodb

I am using the python package pymongo to retrieve data from a mongodb database.
>>> r = collection.find() # returns an object of class 'Cursor'
Then I convert to a list
>>> l = list(r) # returns a 'list' of 'dict'
here is what print(l) returns:
>>> [{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'_id': 1, u'name': u'name1', u'value': 11},{u'date': datetime.datetime(2013, 11, 10, 10, 45), u'_id': 2, u'name': u'name2', u'value': 22}]
Now I need to convert to JSON so that I can manipulate it.
>>> json.dumps(l)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: datetime.datetime(2009, 11, 12, 11, 14) is not JSON serializable
I have also tried to follow http://api.mongodb.org/python/1.7/api/pymongo/json_util.html without success:
Edit: the recent version of the link is http://api.mongodb.org/python/current/api/bson/json_util.html
>>> json.dumps(l, default=json_util.default)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'json_util' is not defined
Note: precisely I need to push this result to R using the R package rPython and its function rPython::python.get("l")
Side Question: What is the u (u'Date', u'name', etc..) before each field in the list of dict?
The pymongo documentation you pointed is obsolete. If you're using version 1.7 I recommend updating. With a more recent version you can do this:
from bson.json_util import dumps
dumps(l)
https://pymongo.readthedocs.io/en/stable/api/bson/json_util.html
Side answer: u'name', u'date', u'_id' etc are the names of the fields of the document on the database.
from bson import json_util
json.dumps(result,default=json_util.default)
In my situation, this error is due to mongo DB id object in flask
all you have to do is convert id (NOTE: if you need id convert it else you can pop it too)
I'm sharing my solution which I figured out hope this helps someone
from flask import jsonify
def get_data(self,data):
data['_id'] = str(data['_id'])
return data
app = Flask(__name__)
#app.route('/')
def apimethod():
temp = [self.get_data(i) for i in self.collection.find()]
return jsonify(temp)
also dumps from pymongo don't help alot
from bson.json_util import dumps,loads
because it is returning a string instead of dict which was expected in my situation to create API and I have to load again if I did dumps.
This thread helped me - thank you.
Wanted to share my final solution to get the JSON back into a JSON/Dictionary Object: (Based on your example)
from bson.json_util import dumps, loads
r = collection.find()
l = list(r) # Converts object to list
d = dumps(l) # Converts to String
dict_needed = loads(d[0]) # Serializes String and creates dictionary
Now you have the JSON in a dictionary object and can edit as needed.
I was facing the same issue, I wrote a code that converts document to dictionary. You can use that for reference. Pass the object obtained by find_one() into documentToJson() method and the results of find() into convertDocumentsToJson. There is type in the name Json, instead the code converts to Dict rather than json.
from bson.json_util import dumps
class UtilService:
def __init__(self):
pass
#staticmethod
def pinCodeParser(path):
location = {}
f = open(path)
for line in f:
words = line.split()
location[words[1]] = (words[-3],words[-2])
return location
#staticmethod
def listHelper(str):
s = []
str = str.split(',')
for e in str:
s.append(e.replace("[","").replace("]",""))
return s
#staticmethod
def parseList(str):
if ',' in str:
return UtilService.listHelper(str)
return str
#staticmethod
def trimStr(str):
return str.replace('"','')
#staticmethod
def documentToJson(document):
document = eval(dumps(document))
mp = {}
for key, value in document.iteritems():
if "_id" in key:
mp["id"] = str(value["$oid"])
else:
mp[ UtilService.trimStr(key) ] = UtilService.parseList( value )
return mp
#staticmethod
def convertDocumentsToJson(documents):
result = []
for document in documents:
result.append(UtilService.documentToJson(document))
return result

Categories

Resources