mongoengine OperationError on saving - python

I'm writing a python script to populate a mongodb database, my models look like the following:
from mongoengine import *
from mongoengine.django.auth import User
class TrackGroup (Document):
name = StringField(required=True)
users = ListField(ReferenceField('User'))
devices = ListField(ReferenceField('Device'))
class Device(Document):
name = StringField(max_length=50, required=True)
grp = ListField(ReferenceField(TrackGroup))
class User(User):
first_name = StringField(max_length=50)
last_name = StringField(max_length=50)
grp = ListField(ReferenceField(TrackGroup))
And my script goes like this:
#Create a group
gName = 'group name'
g = TrackGroup(name=gName)
g.users = []
g.devices = []
g.save()
#create a user
u = User.create_user(username='name', password='admin', email='mail#ex.com')
gRef = g
u.grp = [gRef, ]
u.first_name = 'first'
u.last_name = 'last'
u.save()
gRef.users.append(u)
gRef.save()
#create a device
dev = Device(name='name').save()
gRef = g
dev.grp = [gRef, ]
dev.save()
gRef.devices.append(dev)
gRef.save() #Problem happens here
The problem happens when I call gRef.save() I get the following error:
raise OperationError(message % unicode(err))
mongoengine.errors.OperationError: Could not save document (LEFT_SUBFIELD only supports Object: users.0 not: 7)
I looked around for a while, and here it says that this means I'm trying to set a filed with an empty key, like this: (The example is from the link above, not mine)
{
"_id" : ObjectId("4e52b5e08ead0e3853320000"),
"title" : "Something",
"url" : "http://www.website.org",
"" : "",
"tags" : [ "international"]
}
I don't know where such a field can come from, but I opened a mongo shell and looked at my documents from the three collections, and I couldn't find such a field.
Note: If I add the device first, the same error occurs while saving the group after adding the user.

I had the same error, and this trick work for me:
the_obj_causing_error.reload()
/* make some change */
the_obj_causing_error.price = 5
the_obj_causing_error.save()
just try reload() the object before changing it.

Related

Fill data from database to Word (Django Python)

I don't know how to get data and fill into my word template. It's actually a long list, and I need to fill it on my table on word document. Am I doing it right? Here is my code:
views.py
def save_sample_details(request):
sample = SampleList.objects.all()
doc = DocxTemplate("lab_management/word/sample_template.docx")
context = {
'SNAME' : sample.sample_name,
'STYPE' : sample.sample_type,
'HVER' : sample.hardware_version,
'SVER' : sample.software_version,
'CS' : sample.config_status,
'NUM' : sample.number,
'SNUM' : sample.sample_number,
}
doc.render(context)
doc.save('lab_management/word/sample.docx')
return redirect('/lab/sample/details/')
models.py
class SampleList(models.Model):
sample_name = models.CharField(max_length=32)
sample_type = models.CharField(max_length=32)
hardware_version = models.CharField(max_length=32)
software_version = models.CharField(max_length=32)
config_status = models.CharField(max_length=18)
number = models.IntegerField(default=0)
sample_number = models.CharField(max_length=17)
So if I run this, it shows
'QuerySet' object has no attribute 'sample_name' etc.
You're getting all SampleList objects when you call SampleList.objects.all() so this is returning a QuerySet of zero or more objects, rather than a single object - which is what you want.
Instead you should get the single SampleList object you want: for example sample = SampleList.objects.get(id=1) or another example - to get the first object in the QuerySet you could do sample = SampleList.objects.all()[0] (but if the QuerySet is empty you'll get an index error). You didn't specify how you want to determine which SampleList to put in the Word doc, but you can specify any filters in the .get() properties.
Django has some pretty good documentation covering querysets:
https://docs.djangoproject.com/en/4.1/ref/models/querysets/
Change your code as:
def save_sample_details(request):
sample_list = SampleList.objects.all()
doc = DocxTemplate("lab_management/word/sample_template.docx")
context_list = []
for sample in sample_list:
context = {
'SNAME' : sample.sample_name,
'STYPE' : sample.sample_type,
'HVER' : sample.hardware_version,
'SVER' : sample.software_version,
'CS' : sample.config_status,
'NUM' : sample.number,
'SNUM' : sample.sample_number,
}
context_list.append(context)
doc.render(context_list)
doc.save('lab_management/word/sample.docx')
return redirect('/lab/sample/details/')
You should be iterating in the context_list in the doc as well.
(Assuming you are using docxtpl library)
I have answered my question. Thank you for the help!
views.py
def save_sample_details(request):
sample = SampleList.objects.all()[0:15]
template = DocxTemplate("lab_management/word/sample_template.docx")
context = {
'headers' : ['SNAME', 'STYPE', 'HVER', 'SVER', 'CS', 'NUM', 'SNUM'],
'list': [],
}
alist = ['a']
for i in alist:
for samples in sample:
content= [samples.sample_name, samples.sample_type, samples.hardware_version,
samples.software_version, samples.config_status, samples.number,
samples.sample_number ]
context['list'].append(content)
template.render(context)
template.save('lab_management/word/sample.docx')
return redirect('/lab/sample/details/')
sample_template.docx

Elasticsearch: search does not return documents

When I run the following:
search_doc = Document.search(
using=client,
index=custom_index
)
search_doc = search_doc.query("term", doc_field = f"string_{num}")
and then use .scan() I get Hit objects. So, something seems to be wrong...
However, when I use the ids of those hit objects:
all_docs = [doc.meta.id for doc in search_doc.scan()]
for id in all_docs:
loc = Document.get(
using=client,
index=custom_index,
id = id
)
assert isinstance(doc, Document) # <- always true!!
Why is there this difference? Why doesn't the search object return documents objects?
Here's the definition of my Document class:
field_1 = dsl.Keyword()
field_2 = dsl.Keyword(multi=True)
data = dsl.Object(Inner_Document) # data will have many other fields
last_updated = dsl.Date()
class Index:
name = default_index # different from custom_index
settings = {
"number_of_shards": 1
}
class Meta:
dynamic = dsl.MetaField('strict')
The version of ES that I'm using is 7.17.6 .
The package elasticsearch is 7.17.3
and elasticsearch-dsl is 7.4.0 .

Orator ORM model Create method invalid SQL

I have a database I created with a migration. One of my tables looks like this
def create_customer_table(self):
with self.schema.create("customer") as table:
table.char("name",120).unique()
table.integer("transmitting_hours").default(24) #how many hours after transmission vehicle is considered transmitting
table.boolean("is_tpms").default(False)
table.boolean("is_dor").default(False)
table.boolean("is_otr").default(False)
table.boolean("is_track_and_trace").default(False)
table.char("contact_person",25)
table.char("created_by",25)
table.enum("temperature_unit",TEMP_UNITS)
table.enum("pressure_unit",PRESSURE_UNITS)
table.enum("distance_unit",DISTANCE_UNITS)
table.char("time_zone",25)
table.char("language",2)
table.timestamps()
I have a very simplistic ORM model on top
class Customer(Model):
__table__ = "customer"
__timestamps__ = False
__primary_key__ = "name"
__fillable__ = ['*']
I then try to do a basic insert with the following code
def add_sample_customer():
sample_customer = {}
sample_customer["name"] = "customer_2"
sample_customer["contact_person"] = "Abradolf"
sample_customer["created_by"] = "Frodo"
sample_customer["time_zone"] = "GMT-5"
sample_customer["language"] = "EN"
sample_customer["temperature_unit"] = "FAHRENHEIT"
sample_customer["pressure_unit"] = "PSI"
sample_customer["distance_unit"] = "MI"
customer_model = Customer.create(_attributes = sample_customer)
The exception I get from this code looks like
orator.exceptions.query.QueryException: syntax error at or near ")"
LINE 1: INSERT INTO "customer" () VALUES () RETURNING "name"
(SQL: INSERT INTO "customer" () VALUES () RETURNING "name" ([]))
it looks like orator just isn't filling in the cols and vals here. I have also tried it with a few different syntactic ways of dropping the dict in there, using **sample_customer and also just putting the dict in directly and none of them work, all with the same exception. I started debugging by printing stuff out of the orator libraries but haven't gotten anywhere yet.
my inserts work if I do the model attribute assignment individually and use the model.save() method like this
def add_sample_customer():
sample_customer = {}
sample_customer["name"] = "customer_2"
sample_customer["contact_person"] = "Abradolf"
sample_customer["created_by"] = "Frodo"
sample_customer["time_zone"] = "GMT-5"
sample_customer["language"] = "EN"
sample_customer["temperature_unit"] = "FAHRENHEIT"
sample_customer["pressure_unit"] = "PSI"
sample_customer["distance_unit"] = "MI"
customer_model = Customer()
for k,v in sample_customer.items():
setattr(customer_model,k,v)
customer_model.save()
Does anyone understand why the model.create() syntax fails?
I would think the answer would be:
Simply passing the dictionary instead of using keyword notation with attributes:
Customer.create(sample_customer)
or
Customer.create(attribute=value,attribute2=value2,..etc)
Which are the valid notations

MongoEngine Query Optimization

I have two collections ScenarioDrivers and ModelDrivers which has One to Many relationship with each other.
class ScenarioDrivers(Document):
meta = {
'collection': 'ScenarioDrivers'
}
ScenarioId = ReferenceField('ModelScenarios')
DriverId = ReferenceField('ModelDrivers')
DriverCalibrationMethod = StringField()
SegmentName = StringField()
DriverValue = ListField()
CalibrationStatus = StringField()
AdjustedValues = ListField(default=[])
CreateDate = DateTimeField(default=ObjectId().generation_time)
LastUpdateDate = DateTimeField(default=datetime.utcnow())
class ModelDrivers(Document):
meta = {
'collection': 'ModelDrivers'
}
PortfolioModelId = ReferenceField('PortfolioModels')
DriverName = StringField()
CreateDate = DateTimeField(default=ObjectId().generation_time)
LastUpdateDate = DateTimeField(default=datetime.utcnow())
FieldFormat = StringField()
DriverData = ListField()
My query is like this.
class GetCalibratedDrivers(Resource):
def get(self, scenario_id):
scenario_drivers_list = []
scenario_drivers = ScenarioDrivers.objects(ScenarioId=scenario_id).exclude('ScenarioId').select_related(1)
for scenario_driver in scenario_drivers:
scenario_driver_dict = {
'id': str(scenario_driver.id),
'DriverId': str(scenario_driver.DriverId.id),
'SegmentName': scenario_driver.SegmentName,
'CalibrationMethod': scenario_driver.DriverCalibrationMethod,
'CalibratedValues': exchange(scenario_driver.DriverValue),
'AdjustedValues': scenario_driver.AdjustedValues,
'LastUpdateDate': formatted_date(scenario_driver.LastUpdateDate),
'FieldFormat': scenario_driver.DriverId.FieldFormat
}
scenario_drivers_list.append(scenario_driver_dict)
return {
'DriverCalibrations': scenario_drivers_list
}
The Query matches 1140 records and then I construct a dictionary and make it a list.
But this API call takes 30s to process just 1140 records. Where I am missing? Please help. I am using latest version of Pymongo and MongoEngine.
I think the problem is not with your query, it is with you looping over 1140 records. I do not see any use of referenced objects so you should consider removing select_related(1). Once you do that, if you want to convert reference object ids to string, you can use as_pymongo() which will do that by default for you. And finally if you must read some data in specific format like formatted_date or exchange, it is better to save them as part of your document. i.e. save FormattedLastUpdateDate with LastUpdateDate. In MongoDB, you have to think about your read specific logic when you save the document.

NDB query using filters on Structured property which is also repeated ?

I am creating a sample application storing user detail along with its class information.
Modal classes being used are :
Model class for saving user's class data
class MyData(ndb.Model):
subject = ndb.StringProperty()
teacher = ndb.StringProperty()
strength = ndb.IntegerProperty()
date = ndb.DateTimeProperty()
Model class for user
class MyUser(ndb.Model):
user_name = ndb.StringProperty()
email_id = ndb.StringProperty()
my_data = ndb.StructuredProperty(MyData, repeated = True)
I am able to successfully store data into the datastore and can also make simple query on the MyUser entity using some filters based on email_id and user_name.
But when I try to query MyUser result using filter on a property from the MyUser modal's Structured property that is my_data, its not giving correct result.
I think I am querying incorrectly.
Here is my query function
function to query based upon the repeated structure property
def queryMyUserWithStructuredPropertyFilter():
shail_users_query = MyUser.query(ndb.AND(MyUser.email_id == "napolean#gmail.com", MyUser.my_data.strength > 30))
shail_users_list = shail_users_query.fetch(10)
maindatalist=[]
for each_user in shail_users_list:
logging.info('NEW QUERY :: The user details are : %s %s'% (each_user.user_name, each_user.email_id))
# Class data
myData = each_user.my_data
for each_my_data in myData:
templist = [each_my_data.strength, str(each_my_data.date)]
maindatalist.append(templist)
logging.info('NEW QUERY :: The class data is : %s %s %s %s'% (each_my_data.subject, each_my_data.teacher, str(each_my_data.strength),str(each_my_data.date)))
return maindatalist
I want to fetch that entity with repeated Structured property (my_data) should be a list which has strength > 30.
Please help me in knowing where I am doing wrong.
Thanks.
Queries over StructuredProperties return objects for which at least one of the structured ones satisfies the conditions. If you want to filter those properties, you'll have to do it afterwards.
Something like this should do the trick:
def queryMyUserWithStructuredPropertyFilter():
shail_users_query = MyUser.query(MyUser.email_id == "napolean#gmail.com", MyUser.my_data.strength > 30)
shail_users_list = shail_users_query.fetch(10)
# Here, shail_users_list has at most 10 users with email being
# 'napolean#gmail.com' and at least one element in my_data
# with strength > 30
maindatalist = [
[[data.strength, str(data.date)] for data in user.my_data if data.strength > 30] for user in shail_users_list
]
# Now in maindatalist you have ONLY those my_data with strength > 30
return maindatalist

Categories

Resources