DoesNotExist: [Model] matching query does not exist at id (ObjectId) - python

I am trying to query a unique document using its ObjectId. However the error comes up:
DoesNotExist: Route matching query does not exist
When, upon passing this to my view as request, it prints out the corresponding ObjectId in ObjectId typeform. Therefore there shouldn't be a problem with the line route_test = Route.objects.get(id=_id).
I have the following code:
views.py
def update(request):
if request.method == "POST":
_id = request.POST.get('_id',ObjectId())
print(_id)
route_id = request.POST.get('route_id','')
geometry = request.POST.get('geometry', '')
properties = request.POST.get('properties','')
#r = Route.objects.get(route_id='LTFRB_PUJ2616') --> I cannot use this
#because it has 5 instances (Not Unique)
#print (r["id"],r["properties"])
test = Route.objects.get(id = ObjectId('587c4c3b203ada19e8e0ecf6'))
print (test["id"], test["properties"])
try:
route_test = Route.objects.get(id=_id)
print(route_test)
Route.objects.get(id=_id).update(set__geometry=geometry, set__properties=properties)
return HttpResponse("Success!")
except:
return HttpResponse("Error!")
ajax
var finishBtn = L.easyButton({
id:'finish',
states: [{
icon:"fa fa-check",
onClick: function(btn){
selectedFeature.editing.disable();
layer.closePopup();
var editedFeature = selectedFeature.toGeoJSON();
alert("Updating:" + editedFeature.route_id);
$.ajax({
url: "/update/",
data: {id:editedFeature.id,
route_id: JSON.stringify(editedFeature.route_id),
geometry: JSON.stringify(editedFeature.geometry),
properties: JSON.stringify(editedFeature.properties)
},
type: 'POST'
});
}
model.py
from __future__ import unicode_literals
from mongoengine import *
class Route(Document):
type = StringField(required=True)
route_id = StringField(required=True)
geometry = LineStringField()
properties = DictField()
meta = {'collection':'routes'}
What should be done? Even the line test = Route.objects.get(id = ObjectId('587c4c3b203ada19e8e0ecf6')) where I directly supplied the incoming _id has the same error...

Related

How to use postman to upload DBRef data in json?

I'm trying to post data to mongodb using postman but I don't know the proper convention for uploading the reference to a image file in the fs.files bucket. Basically, the file is already in the database, I'm just trying to post a new user with the reference to the image.
Here is my model:
class Users(db.Document):
_id = db.StringField()
name = db.StringField()
picture = db.FileField()
email = db.StringField()
password = db.StringField()
meta = {'collection': 'Users'}
In postman, I try to post data like so:
{
"_id" : "1",
"name" : "John Doe",
"picture": [{"$id": "5e6a...f9q102"}], #This is the reference id for the image already in the database, in fs.files
"password" : "<hashed pw>",
"email" : "example#example.com"
}
I'm using flask restful api so in the python script, the post function is defined like so:
def post(self):
body = request.get_json()
print (body)
user = Users()
user = Users(**body).save()
return 'Successful Upload', 200
I get the error when I try with the above convention:
mongoengine.errors.ValidationError: ValidationError (Users:None) ('list' object has no attribute
'grid_id': ['picture'])
How do I post a new user in postman? Your help is appreciated
You need to change a bit your code
Add these imports:
from mongoengine.fields import ImageGridFsProxy
from mongoengine import ReferenceField, DynamicDocument
from bson.dbref import DBRef
from bson import ObjectId
Modify your class picture field definition + add extra class fs
class Fs(DynamicDocument):
#Add 'db_alias':'default' to meta
meta = {'collection': 'fs.files'}
class Users(Document):
...
picture = ReferenceField('Fs', dbref=True)
...
Now, you need to create new instance for DBRef this way:
def post(self):
body = request.get_json()
body["picture"] = DBRef('fs.files', ObjectId(body["picture"]))
#mongoengine assumes `ObjectId('xxx')` already exists in `fs.files`.
#If you want to check, run below code:
#if Fs.objects(_id=body["picture"].id).first() is None:
# return 'Picture ' + str(body["picture"].id) + ' not found', 400
user = Users(**body).save()
return 'Successful Upload', 200
At the end, if you need to read picture content:
image = ImageGridFsProxy(grid_id=ObjectId('xxx'))
f = open("image.png", "wb")
f.write(image.read())
f.close()
It was a Validation error. The database was accepting JSON in a particular format than what I was posting. And the way I was processing the post request was also incorrect. This is the format it expected:
{
...,
"picture" = {"$ref": "fs.files",
"$id": ObjectId("5e6a...f9q102")},
...
}
Postman cannot accept the above format, instead, it accepted this:
{
"_id" : "1",
"name" : "John Doe",
"picture": {"$ref": "fs.files", "$id": {"$oid": "5e6a...f9q102"}},
"password" : "<hashed pw>",
"email" : "example#example.com"
}
To make this work I changed the model to look this so in my flask app:
class Users(db.Document):
_id = db.StringField()
name = db.StringField()
picture = db.ReferenceField('fs.files') #I changed this to a reference field because it holds the reference for the file and not the actual file in the database
upload_picture = db.FileField() #I added this field so I can still upload pics via flask and via this document
email = db.StringField()
password = db.StringField()
meta = {'collection': 'Users'}
Then I had to add this import and change the code so that it would read the input as JSON and transfer the reference value of picture to ObjectId(id) so that it matches the format the database was expecting.
from bson.json_util import loads
def post(self):
body = str(request.get_json())
x = body.replace("'", '"') #replace single quotes as double quotes to match JSON format
data = loads(x)
officer = Officers(**data).save()
return 'Successful Upload', 200
Then voila, it works!

How to use SELECT query based on method parameter in Flask restplus Api?

I want to handle a GET request in my Flask REST API.The request will include List parameter and I use this to be a typical GET request:
https://localhost:5000/organisation?List=1
this request, will return all of Organisation Model information. Organisation is a Model class for a table of my MySQL database(by peewee).
I want to enter PrivenceId parameter in request and will return all of Organisation Model information where Organisation.PrivencedId == PrivenceId parameter, But I encounter the following Error:
TypeError: 'Organisation' object is not iterable
my code (OrganisationController.py file) is:
from flask_restplus import Resource, reqparse
from Models.Organisation import Organisation
from flask import request
# Define parser and request args
parser = reqparse.RequestParser()
parser.add_argument('List', type=int)
parser.add_argument('ProvinceId', type=int)
class List(Resource):
def get(self):
args = parser.parse_args()
List = args['List']
ProvinceId = args['ProvinceId']
if List :
Organisations = Organisation.select()
Ls = [dict (
ID = c.ID,
Title = c.Title,
Code = c. Code,
OrgEnumId = c.OrgEnumId,
ProvinceId = c.ProvinceId,
CityId = c.CityId,
Address = c.Address,
Phone = c.Phone,
) for c in Organisations
]
return dict(Organisations = Ls)
elif (ProvinceId) :
Organisations = Organisation.select().where
(
Organisation.ProvinceId ==args['ProvinceId']
).get()
Ls = [dict (
ID = c.ID,
Title = c.Title,
Code = c. Code,
OrgEnumId = c.OrgEnumId,
ProvinceId = c.ProvinceId,
CityId = c.CityId,
Address = c.Address,
Phone = c.Phone,
) for c in Organisations
]
return dict(Organisations = Ls)
and boot.py file for run api is :
from flask import Flask
from flask_restplus import Api
from Controllers import OrganisationController
app = Flask(__name__)
api = Api(app)
api.add_resource(OrganisationController.List, '/organisation')
if __name__ == "__main__":
app.run ('127.0.0.1', 5000, True)
Could you help me?
I changed this line of my code and solved my problem:
Organisations = Organisation.select().where
(
Organisation.ProvinceId ==args['ProvinceId']
)

How to change the printed qweb filename

How to change the printed qweb filename depends on the source document field name?
Im in the model stock.picking
So when i click the print -> Delivery note, then the qweb will be printed, but te filename will be depends on the Source document field.
Here i present the picture that explain what i mean.
EXAMPLE
You can give dynamic report name using configuration, but it will apply when you print one report.
Below is the example to Print custom name in report.Create one field in ir.actions.report.xml, in which user can configure report name.
from openerp import models, fields
class IrActionsReportXml(models.Model):
_inherit = 'ir.actions.report.xml'
download_filename = fields.Char(
'Download filename')
Now you need to create two files.
Report Controller
from openerp import http
from openerp.addons.mail.models import mail_template
from openerp.addons.report.controllers.main import ReportController
from openerp.addons.web.controllers.main import content_disposition
class ReportController(ReportController):
#http.route([
'/report/<path:converter>/<reportname>',
'/report/<path:converter>/<reportname>/<docids>',
])
def report_routes(self, reportname, docids=None, converter=None, **data):
response = super(ReportController, self).report_routes(
reportname, docids=docids, converter=converter, **data)
if docids:
docids = [int(i) for i in docids.split(',')]
report_xml = http.request.session.model('ir.actions.report.xml')
report_ids = report_xml.search(
[('report_name', '=', reportname)])
for report in report_xml.browse(report_ids):
if not report.download_filename:
continue
objects = http.request.session.model(report.model)\
.browse(docids or [])
generated_filename = mail_template.mako_template_env\
.from_string(report.download_filename)\
.render({
'objects': objects,
'o': objects[:1],
'object': objects[:1],
'ext': report.report_type.replace('qweb-', ''),
})
response.headers['Content-Disposition'] = content_disposition(
generated_filename)
return response
#http.route(['/report/download'])
def report_download(self, data, token):
response = super(ReportController, self).report_download(data, token)
# if we got another content disposition before, ditch the one added
# by super()
last_index = None
for i in range(len(response.headers) - 1, -1, -1):
if response.headers[i][0] == 'Content-Disposition':
if last_index:
response.headers.pop(last_index)
last_index = i
return response
2.Report.py
import json
from openerp import http
from openerp.addons.web.controllers import main
from openerp.addons.mail.models import mail_template
class Reports(main.Reports):
#http.route('/web/report', type='http', auth="user")
#main.serialize_exception
def index(self, action, token):
result = super(Reports, self).index(action, token)
action = json.loads(action)
context = dict(http.request.context)
context.update(action["context"])
report_xml = http.request.env['ir.actions.report.xml']
reports = report_xml.search([
('report_name', '=', action['report_name']),
('download_filename', '!=', False)])
for report in reports:
objects = http.request.session.model(context['active_model'])\
.browse(context['active_ids'])
generated_filename = mail_template.mako_template_env\
.from_string(report.download_filename)\
.render({
'objects': objects,
'o': objects[0],
'object': objects[0],
})
result.headers['Content-Disposition'] = main.content_disposition(
generated_filename)
return result
Odoo community Providing us a default module for report custom name. you can directly install this module and set report name like : ${o.name}
Here o means your record.
Below is a link of odoo community module.
https://www.odoo.com/apps/modules/9.0/report_custom_filename/
This may help you.
try attachment in your reprot to change report file name as your desired name.
<report
string="Delivery Slip"
id="action_report_delivery"
model="stock.picking"
report_type="qweb-pdf"
name="stock.report_deliveryslip"
file="stock.report_deliveryslip"
attachment="'Custom Text...'+'.pdf')"
/>
Here is my code which works for different reportname in the same model. So the filename only changes for the certain filename.
>
from openerp.addons.web.http import Controller, route, request
from openerp.addons.web.controllers.main import _serialize_exception
from openerp.osv import osv
from openerp.addons.report.controllers.main import ReportController
from openerp import http
import simplejson
import logging
class SponsorReportController(ReportController):
#route(['/report/download'], type='http', auth="user")
def report_download(self, data, token):
requestcontent = simplejson.loads(data)
url, type = requestcontent[0], requestcontent[1]
logging.info(url)
logging.info(type)
response = ReportController().report_download(data, token)
if len(url.split('/report/pdf/')) > 1 and type == 'qweb-pdf':
reportname = url.split('/report/pdf/')[1].split('?')[0]
reportname, docids = reportname.split('/')
logging.info(reportname)
assert docids
logging.info(docids)
if reportname == 'pci_stock_picking_report.report_delivery_note':
partner_obj = http.request.env['stock.picking']
object = partner_obj.browse(int(docids))
filename = "No."+(object.origin)
response.headers.set('Content-Disposition', 'attachment; filename=%s.pdf;' % filename)
return response

'ManyToOneRel' object has no attribute 'parent_model' error with django chartit

I have django project with 2 models:
class DeviceModel(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
class Device(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
device_model = models.ForeignKey(DeviceModel)
serial_number = models.CharField(max_length=255)
def __unicode__(self):
return self.device_model.name + " - " + self.serial_number
There many devices in the database and I want to plot chart "amount of devices" per "device model".
I'm trying to do this task with django chartit.
Code in view.py:
ds = PivotDataPool(
series=[
{'options': {
'source':Device.objects.all(),
'categories':'device_model'
},
'terms':{
u'Amount':Count('device_model'),
}
}
],
)
pvcht = PivotChart(
datasource=ds,
series_options =
[{'options':{
'type': 'column',
'stacking': True
},
'terms':[
u'Amount']
}],
chart_options =
{'title': {
'text': u'Device amount chart'},
'xAxis': {
'title': {
'text': u'Device name'}},
'yAxis': {
'title': {
'text': u'Amount'}}}
)
return render(request, 'charts.html', {'my_chart': pvcht})
This seems to plot result I need, but instead of device names it plots values of ForeignKey (1,2,3,4...) and I need actual device model names.
I thought that solution is to change 'categories' value to:
'categories':'device_model__name'
But this gives me error:
'ManyToOneRel' object has no attribute 'parent_model'
This type of referencing should work accoring to official example http://chartit.shutupandship.com/demo/pivot/pivot-with-legend/
What am I missing here?
C:\Anaconda\lib\site-packages\django\core\handlers\base.py in get_response
response = middleware_method(request, callback, callback_args, callback_kwargs)
if response:
break
if response is None:
wrapped_callback = self.make_view_atomic(callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs) ###
except Exception as e:
# If the view raised an exception, run it through exception
# middleware, and if the exception middleware returns a
# response, use that. Otherwise, reraise the exception.
for middleware_method in self._exception_middleware:
response = middleware_method(request, e)
D:\django\predator\predator\views.py in charts
series=[
{'options': {
'source':Device.objects.all(),
'categories':'device_model__name'
},
#'legend_by': 'device_model__device_class'},
'terms':{
u'Amount':Count('device_model'), ###
}
}
],
#pareto_term = 'Amount'
)
C:\Anaconda\lib\site-packages\chartit\chartdata.py in __init__
'terms': {
'asia_avg_temp': Avg('temperature')}}]
# Save user input to a separate dict. Can be used for debugging.
self.user_input = locals()
self.user_input['series'] = copy.deepcopy(series)
self.series = clean_pdps(series) ###
self.top_n_term = (top_n_term if top_n_term
in self.series.keys() else None)
self.top_n = (top_n if (self.top_n_term is not None
and isinstance(top_n, int)) else 0)
self.pareto_term = (pareto_term if pareto_term in
self.series.keys() else None)
C:\Anaconda\lib\site-packages\chartit\validation.py in clean_pdps
def clean_pdps(series):
"""Clean the PivotDataPool series input from the user.
"""
if isinstance(series, list):
series = _convert_pdps_to_dict(series)
clean_pdps(series) ###
elif isinstance(series, dict):
if not series:
raise APIInputError("'series' cannot be empty.")
for td in series.values():
# td is not a dict
if not isinstance(td, dict):
C:\Anaconda\lib\site-packages\chartit\validation.py in clean_pdps
try:
_validate_func(td['func'])
except KeyError:
raise APIInputError("Missing 'func': %s" % td)
# categories
try:
td['categories'], fa_cat = _clean_categories(td['categories'],
td['source']) ###
except KeyError:
raise APIInputError("Missing 'categories': %s" % td)
# legend_by
try:
td['legend_by'], fa_lgby = _clean_legend_by(td['legend_by'],
td['source'])
C:\Anaconda\lib\site-packages\chartit\validation.py in _clean_categories
else:
raise APIInputError("'categories' must be one of the following "
"types: basestring, tuple or list. Got %s of "
"type %s instead."
%(categories, type(categories)))
field_aliases = {}
for c in categories:
field_aliases[c] = _validate_field_lookup_term(source.model, c) ###
return categories, field_aliases
def _clean_legend_by(legend_by, source):
if isinstance(legend_by, basestring):
legend_by = [legend_by]
elif isinstance(legend_by, (tuple, list)):
C:\Anaconda\lib\site-packages\chartit\validation.py in _validate_field_lookup_term
# and m2m is True for many-to-many relations.
# When 'direct' is False, 'field_object' is the corresponding
# RelatedObject for this field (since the field doesn't have
# an instance associated with it).
field_details = model._meta.get_field_by_name(terms[0])
# if the field is direct field
if field_details[2]:
m = field_details[0].related.parent_model ###
else:
m = field_details[0].model
return _validate_field_lookup_term(m, '__'.join(terms[1:]))
def _clean_source(source):
In categories you can only use fields that are included in source queryset.
On the other hand in terms you can use foreignKey or manyTomany connected fields.
Find an example below.
Instead of using
'source':Device.objects.all()
'categories':'device_model'
try to use
'source':DeviceModel.objects.all()
'categories':'name'
and next
'Amount':Count('device__device_model')
I think there is a problem with newer version of django (1.8).
This code is deprecated:
m = field_details[0].related.parent_model
instead of it use
m = field_details[0].getattr(field.related, 'related_model', field.related.model)
You can also find fix to this problem on GitHub.
Hope this helps.

send report in specific json format

views.py
def json(request):
defaultnumber = []
phoneinfo = PhoneInfo.objects.filter(user = user_id)
for phone in phoneinfo:
phone_no = {'id':some.id,
'name1':phone.name1,
'number1':phone.number1,
'name2':phone.name2,
'number2':phone.number2,
} }
defaultnumber.append(phone_no)
result = { 'phone':defaultnumber}
return HttpResponse(json.dumps(result), mimetype="application/json")
I need to send the data into json format.
Use model_to_dict instead:
from django.forms.models import model_to_dict
def json_view(request):
phoneinfo = PhoneInfo.objects.filter(user = user_id)
phones = [model_to_dict(phone) for phone in phoneinfo]
result = {'phoneinfo': phones}
return HttpResponse(json.dumps(result), mimetype="application/json")
And, don't call view json - you are overriding json module name.
And, it's unclear from where user_id variable comes.

Categories