I'm using Plone with dexterity and I'm validating 2 related fields using the invariant decorator. Everything works but... I'd like to move the generic error message on one specific field.
How can I do this? I've found a three years old suggestion by Martin Aspeli about how it would be cool doing it:
http://plone.293351.n2.nabble.com/plone-app-form-does-not-display-invariant-errors-td348710.html
but they dont came out with a solution.
I've also found a way to do it but it's ugly: placing on the update method of the form this code:
for widget in widgets:
name = widget.context.getName()
if errors:
for error in errors:
if isinstance(error, Invalid) and name in error.args[1:]:
if widget._error is None:
widget._error = error
Isn't there a lower level implementation that allows to pass the names of the fields to the raised Invalid and does not need to loop trough all the fields and all the errors for each field?!?
You can do this by doing your extra validation in the form's action handler, and raise WidgetActionExecutionError specifying the widget for which the error should be shown.
This looks as follows (taken from http://plone.org/products/dexterity/documentation/manual/schema-driven-forms/customising-form-behaviour/validation):
from five import grok
from plone.directives import form
from zope.interface import invariant, Invalid
from zope import schema
from z3c.form import button
from z3c.form.interfaces import ActionExecutionError, WidgetActionExecutionError
from Products.CMFCore.interfaces import ISiteRoot
from Products.statusmessages.interfaces import IStatusMessage
from example.dexterityforms.interfaces import MessageFactory as _
...
class OrderForm(form.SchemaForm):
...
#button.buttonAndHandler(_(u'Order'))
def handleApply(self, action):
data, errors = self.extractData()
# Some additional validation
if 'address1' in data and 'address2' in data:
if len(data['address1']) < 2 and len(data['address2']) < 2:
raise ActionExecutionError(Invalid(_(u"Please provide a valid address")))
elif len(data['address1']) < 2 and len(data['address2']) > 10:
raise WidgetActionExecutionError('address2', Invalid(u"Please put the main part of the address in the first field"))
if errors:
self.status = self.formErrorsMessage
return
I think it may also be possible to raise WidgetActionExecutionError from your invariant, but then it might not do what you want if the invariant is getting checked at some other time than while processing a z3c.form form.
Related
I have recently started to learn and work on Django i came across this error multiple times sometimes it was due to repetitive use of a name in an html and sometimes it was due the part for example "variable_name=request.POST['variable_name']"
I visited various sites regarding this error and was able to find a solution i.e, once i have typed in "variable_name=request.POST['variable_name']" in a function in views.py
the next time i tried to POST the data again "variable_name=request.POST['variable_name']" I had to do something like this "variable_name=request.POST.get('variable-name', 'blank_or_whatever_canBeFalseAlso')
The Above statement helped me solve that error I got this solution from A Thread in StackOverflow itself Quite a useful site.
Now for the Question am just Curious to know whats the difference between
Variable_Name=request.POST['Variable_Name']
Variable_Name=request.POST.get('Variable_Name','Whatever')
Code (views.py) Is given Below:
from django.shortcuts import render
from . import views
from datetime import date
from .models import Batch, Deleted_Batch
from django.utils.datastructures import MultiValueDictKeyError
# Create your views here.
today = date.today()
Date_Batch = str(today.day)+'/'+str(today.month)+'/'+str(today.year)
def Reg_Batch(request):
return render(request, 'Reg_Batch.html',{'Date_Batch':Date_Batch})
def Reg_Batch_Receive(request):
Region_Code=request.POST['Region_Code']
Branch_Code=request.POST['Branch_Code']
Batch_Number=request.POST['Batch_Number']
Farm_Code=request.POST['Farm_Code']
Farm_Status=request.POST['Farm_Status']
Farmer_Name=request.POST['Farmer_Name']
Farmer_Address=request.POST['Farmer_Address']
Farmer_Phone_Number=request.POST['Farmer_Phone_Number']
Farmer_Email=request.POST['Farmer_Email']
return render(request, 'Reg_Batch_Receive.html')
def Reg_Batch_View(request):
Region_Code=request.POST.get('Region_Code', 'Blank')
Branch_Code=request.POST.get('Branch_Code', 'Blank')
Farm_Code=request.POST.get('Farm_Code', 'Blank')
Farm_Status=request.POST.get('Farm_Status', 'Blank')
return render(request, 'Reg_Batch_View.html',{'Region_Code':Region_Code,'Branch_Code':Branch_Code,'Farm_Code':Farm_Code,'Farm_Status':Farm_Status})
def Reg_Batch_View_Receive(request):
Date_Batch= '20/08/2000'
Region_Code='586001'
Branch_Code='586001'
Batch_Number='586001'
Farm_Code='5484712'
Farm_Status='Active'
Farmer_Name='Joel Liao'
Farmer_Address='jojojojojojojo'
Farmer_Phone_Number='8088078086'
Farmer_Email='kuroneko#gmail.com'
return render(request,'Reg_Batch_View_Receive.html',{'Date_Batch':Date_Batch,'Region_Code':Region_Code,'Branch_Code':Branch_Code,'Batch_Number':Batch_Number,'Farm_Code':Farm_Code,'Farm_Status':Farm_Status,'Farmer_Name':Farmer_Name,'Farmer_Address':Farmer_Address,'Farmer_Phone_Number':Farmer_Phone_Number,'Farmer_Email':Farmer_Email})
def Reg_Batch_Delete(request):
Region_Code='586001'
Branch_Code='586001'
Batch_Number='586001'
Farm_Code='5484712'
Farm_Status='Active'
Farmer_Name='Joel Liao'
Farmer_Address='jojojojojojojo'
Farmer_Phone_Number='8088078086'
Farmer_Email='kuroneko#gmail.com'
Deleted_Batch_DataBase=Deleted_Batch({'Region_Code':Region_Code,'Branch_Code':Branch_Code,'Batch_Number':Batch_Number,'Farm_Code':Farm_Code,'Farm_Status':Farm_Status,'Farmer_Name':Farmer_Name,'Farmer_Address':Farmer_Address,'Farmer_Phone_Number':Farmer_Phone_Number,'Farmer_Email':Farmer_Email}),
return render(request,'Reg_Batch_Delete.html',{'Batch_Number':Batch_Number})
def Reg_Batch_Delete_Receive(request):
Batch_Number='586001'
return render(request,'Reg_Batch_Delete_Receive.html',{'Batch_Number':Batch_Number})
request.POST is a dictionary-like object (see docs).
So if you do
request.POST['foobar']
you can access the posted variable foobar. But if this is not being posted and therefore not set inside POST, you will get the MultiValueDictKeyError.
This is similar to access a key inside a default dictionary which is not set, e.g.
d = {}
d['foobar'] # raises Exception
In order to avoid this error, you can then do
request.POST.get('foobar', 'default-value')
in order to get the posted field and a default if it is not present.
So if foobar isn't set inside your POST-data, the received value is default-value.
This is similar to the default dictionary handling:
d = {}
d.get('foobar', 'random') # returns 'random'
Whenever my Spyne application receives a request, XSD validation is performed. This is good, but whenever there is an XSD violation a fault is raised and my app returns a Client.SchemaValidationError like so:
<soap11env:Fault>
<faultcode>soap11env:Client.SchemaValidationError</faultcode>
<faultstring>:25:0:ERROR:SCHEMASV:SCHEMAV_CVC_DATATYPE_VALID_1_2_1: Element '{http://services.sp.pas.ng.org}DateTimeStamp': '2018-07-25T13:01' is not a valid value of the atomic type 'xs:dateTime'.</faultstring>
<faultactor></faultactor>
</soap11env:Fault>
I would like to know how to handle the schema validation error gracefully and return the details in the Details field of my service's out_message, rather than just raising a standard Client.SchemaValidationError. I want to store the details of the error as a variable and pass it to my OperationOne function.
Here is my code, I have changed var names for sensitivity.
TNS = "http://services.so.example.org"
class InMessageType(ComplexModel):
__namespace__ = TNS
class Attributes(ComplexModel.Attributes):
declare_order = 'declared'
field_one = Unicode(values=["ONE", "TWO"],
min_occurs=1)
field_two = Unicode(20, min_occurs=1)
field_three = Unicode(20, min_occurs=0)
Confirmation = Unicode(values=["ACCEPTED", "REJECTED"], min_occurs=1)
FileReason = Unicode(200, min_occurs=0)
DateTimeStamp = DateTime(min_occurs=1)
class OperationOneResponse(ComplexModel):
__namespace__ = TNS
class Attributes(ComplexModel.Attributes):
declare_order = 'declared'
ResponseMessage = Unicode(values=["SUCCESS", "FAILURE"], min_occurs=1)
Details = Unicode(min_len=0, max_len=2000)
class ServiceOne(ServiceBase):
#rpc(InMessageType,
_returns=OperationOneResponse,
_out_message_name='OperationOneResponse',
_in_message_name='InMessageType',
_body_style='bare',
)
def OperationOne(ctx, message):
# DO STUFF HERE
# e.g. return {'ResponseMessage': Failure, 'Details': XSDValidationError}
application = Application([ServiceOne],
TNS,
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11(),
name='ServiceOne',)
wsgi_application = WsgiApplication(application)
if __name__ == '__main__':
pass
I have considered the following approach but I can't quite seem to make it work yet:
create subclass MyApplication with call_wrapper() function overridden.
Instantiate the application with in_protocol=Soap11(validator=None)
Inside the call wrapper set the protocol to Soap11(validator='lxml') and (somehow) call something which will validate the message. Wrap this in a try/except block and in case of error, catch the error and handle it in whatever way necessary.
I just haven't figured out what I can call inside my overridden call_wrapper() function which will actually perform the validation. I have tried protocol.decompose_incoming_envelope() and other such things but no luck yet.
Overriding the call_wrapper would not work as the validation error is raised before it's called.
You should instead use the event subsystem. More specifically, you must register an application-level handler for the method_exception_object event.
Here's an example:
def _on_exception_object(ctx):
if isinstance(ctx.out_error, ValidationError):
ctx.out_error = NicerValidationError(...)
app = Application(...)
app.event_manager.add_listener('method_exception_object', _on_exception_object)
See this test for more info: https://github.com/arskom/spyne/blob/4a74cfdbc7db7552bc89c0e5d5c19ed5d0755bc7/spyne/test/test_service.py#L69
As per your clarification, if you don't want to reply with a nicer error but a regular response, I'm afraid Spyne is not designed to satisfy that use-case. "Converting" an errored-out request processing state to a regular one would needlessly complicate the already heavy request handling logic.
What you can do instead is to HACK the heck out of the response document.
One way to do it is to implement an additional method_exception_document event handler where the <Fault> tag and its contents are either edited to your taste or even swapped out.
Off the top of my head:
class ValidationErrorReport(ComplexModel):
_type_info = [
('foo', Unicode),
('bar', Integer32),
]
def _on_exception_document(ctx):
fault_elt, = ctx.out_document.xpath("//soap11:Fault", namespaces={'soap11': NS_SOAP11_ENV})
explanation_elt = get_object_as_xml(ValidationErrorReport(...))
fault_parent = fault_elt.parent()
fault_parent.remove(fault_elt)
fault_parent.add(explanation_elt)
The above needs to be double-checked with the relevant Spyne and lxml APIs (maybe you can use find() instead of xpath()), but you get the idea.
Hope that helps!
I am using Rasa core v10.2, with custom actions, but getting utter_template() missing 1 required positional argument: 'tracker' error on the run function.
Custom action file: (actions.py)
from rasa_core.actions import Action
from rasa_core.events import SlotSet
class searchJob(Action):
def name(self):
return 'action_search'
def run(self, dispatcher, tracker, domain):
return [SlotSet("jobsname", "as_aggregate")]
domain.yml:
slots:
jobsname:
type: text
templates:
utter_answer:
- text: "The required jobs are {jobsname}"
Stories:
* search
- action_search
- utter_answer
The output is [{'recipient_id': 'default', 'text': 'The required jobs are None'}] which means the Slot it not set. Could the error message be the cause of it?
Thank you.
Are you sure you are looking at the right custom action? I had the same error message because in one of my custom action I was using dispatcher.utter_template() like this:
dispatcher.utter_template('utter_something')
and apparently tracker is now a required argument, i.e. I add tracker like this:
dispatcher.utter_template('utter_something', tracker)
and the error is gone.
PS: I know this should be a comment, but I am not allowed since my reputation is not high enough.
So the issue seems to be the name of the custom action file, change it to anything else from 'actions' will do.
I am using Odoo 11 and I want to display a Warning popup in a #api.constraint method. The warning popup that I want to display has two buttons, the first is an OK button used to ignore the warning and the other one is a Cancel button used to attempt the saving operation, it' s similar to the warning popup that odoo uses like on the picture below :
I searched a lot on the web and I found different proposed solutions like using Wizard, exception.Warning() and osv.except_osv(), but unfortunately no one of this solution gives me exactly what I want.
Any help please?
You can raise warning message by different ways. I have created message related to stock quantity by this way:
if self.qty > new_qty:
message = _('You plan to sell %s quantity but you only have %s available in %s warehouse.') % \
(self.qty, self.product_id.virtual_available, self.order_id.warehouse_id.name)
mess= {
'title': _('Not enough inventory!'),
'message' : message
}
return {'warning': mess}
This return same wizard of warning as you want and as shown given image.
The basic odoo Warning that you can use is called from the odoo.exception class.
For example:
from odoo.exceptions import AccessError, UserError, RedirectWarning, ValidationError, Warning
#api.constrains('age')
def _check_something(self):
for record in self:
if record.age > 20:
raise Warning(_("Your record is too old: %s" % record.age))
This should work for your issue.
I write a (draft) module to open Dialog box see:
https://github.com/Micronaet/micronaet-sales/tree/master/confirm_dialog_wizard
In your button you can write this code to open, for ex. to hide a product:
#api.multi
def hide_product_pricelist(self):
""" Hide product
"""
return self.env['dialog.box.wizard'].open_dialog(
message='The product will be hided, <b>you cannot use again</b> '
'but remain in sale order where yet present, <br/>'
'confirm?',
action='self.env["product.product"].browse(%s).write('
'{"active": False})' % self.id,
title='Confirm request:',
mode='cancel_confirm',
)
The program will pop up a window to confirm (as in tree you cannot use "confirm" message parameter), I'm try to do better but ... its a begin ... :)
It is similar to the warning shown when we use the confirm attribute on a button.
<button confirm="Reset to draft!"/>
Raising a validation error or returning a warning as dictionary did not show the Cancel button.
My question concerns a class that I am writing that may or may not be fully initialized. The basic goal is to take a match_id and open the corresponding match_url (example: http://dota2lounge.com/match?m=1899) and then grab some properties out of the webpage. The problem is some match_ids will result in 404 pages (http://dota2lounge.com/404).
When this happens, there won't be a way to determine the winner of the match, so the rest of the Match can't be initialized. I have seen this causing problems with methods of the Match, so I added the lines to initialize everything to None if self._valid_url is False. This works in principal, but then I'm adding a line each time a new attribute is added, and it seems prone to errors down the pipeline (in methods, etc.) It also doesn't alert the user that this class wasn't properly initialized. They would need to call .is_valid_match() to determine that.
tl;dr: What is the best way to handle classes that may be only partially initiated? Since this is a hobby project and I'm looking to learn, I'm open to pretty much any solutions (trying new things), including other classes or whatever. Thanks.
This is an abbreviated version of the code containing the relevant portions (Python 3.3):
from urllib.request import urlopen
from bs4 import BeautifulSoup
class Match(object):
def __init__(self, match_id):
self.match_id = match_id
self.match_url = self.__determine_match_url__()
self._soup = self.__get_match_soup__()
self._valid_match_url = self.__determine_match_404__()
if self._valid_match_url:
self.teams, self.winner = self.__get_teams_and_winner__()
# These lines were added, but I'm not sure if this is correct.
else:
self.teams, self.winner = None, None
def __determine_match_url__(self):
return 'http://dota2lounge.com/match?m=' + str(self.match_id)
def __get_match_soup__(self):
return BeautifulSoup(urlopen(self.match_url))
def __get_match_details__(self):
return self._soup.find('section', {'class': 'box'})
def __determine_match_404__(self):
try:
if self._soup.find('h1').text == '404':
return False
except AttributeError:
return True
def __get_teams_and_winner__(self):
teams = [team.getText() for team in
self._soup.find('section', {'class': 'box'}).findAll('b')]
winner = False
for number, team in enumerate(teams):
if ' (win)' in team:
teams[number] = teams[number].replace(' (win)', '')
winner = teams[number]
return teams, winner
def is_valid_match(self):
return all([self._valid_match_url, self.winner])
I would raise an exception, handle that in your creation code (wherever you call some_match = Match(match_id)), and probably don't add it to whatever list you may or may not be using...
For a better answer, you might want to include in your question the code that instantiates all your Match objects.