In WTForms, how do I make optionally required field if another one is empty? - forms

I have two fields where user can choose one to enter but they can't enter both and they can't skip both either. Requiered() makes it that they can't skip one and Optional() let them skip both.
I can only find an example for when you want another to be mandatory when you fill one field but I don't know how to modify it to my case because the example inherits a Required() validator which makes the two fields both required.
Following is the example I found here. Does anybody know if there's a native way to do what I described now or know how to modify this to suit my case?
class RequiredIf(Required):
# a validator which makes a field required if
# another field is set and has a truthy value
def __init__(self, other_field_name, *args, **kwargs):
self.other_field_name = other_field_name
super(RequiredIf, self).__init__(*args, **kwargs)
def __call__(self, form, field):
other_field = form._fields.get(self.other_field_name)
if other_field is None:
raise Exception('no field named "%s" in form' % self.other_field_name)
if bool(other_field.data):
super(RequiredIf, self).__call__(form, field)

Related

How Can I Verify Text Amount in Katalon

I'm trying to verify a text in Katalon and my script isn't working.
Here's my element:
<span id="overviewTabStoreCredit" class="h2 strong amountCredit text-danger">-$100.00</span>
Here's my script:
def StoreCreditAmount = '-$100.00'
TestObject StoreCreditTO = findTestObject('Baseline/Page_Side Menu/Page_Customers/Page_Customer Card/span_Verify Credit Limit')
WebUI.verifyElementAttributeValue(StoreCreditTO, 'text', StoreCreditAmount, GlobalVariable.G_Timeout_Tiny, FailureHandling.CONTINUE_ON_FAILURE)
When running the script, I get an error message, "Object does not have attribute 'text'"
I also tried this to character it by class instead of text:
def StoreCreditAmount = 'h2 strong amountCredit text-danger'
TestObject StoreCreditTO = findTestObject('Baseline/Page_Side Menu/Page_Customers/Page_Customer Card/span_Verify Credit Limit')
WebUI.verifyElementAttributeValue(StoreCreditTO, 'class', StoreCreditAmount, GlobalVariable.G_Timeout_Tiny, FailureHandling.CONTINUE_ON_FAILURE)
I got this error:
Has attribute 'class' with actual value 'text-success h2 strong amountCredit' instead of expected value 'h2 strong amountCredit text-danger' even though the value is correct.
'Text' might not be an attribute. You can getText() from the element and then compare with the expected result. Sometimes, the value you see might not from Text, but from the attribute 'value'.
When you look at your tag there is no "text" attribute:
<span id="overviewTabStoreCredit" class="h2 strong amountCredit text-danger">
Some elements (like text-boxes) have hidden "value" elements for input text, but that is not the case here.
I believe what you want to do is check that the text between your tags equals a certain amount, in this case: "-$100.00".
To check the text between your opening/closing tags for your element use
WebUI.getText(). So your code could grab the text between the tags of your element, and then do an assert (or do it in one step) to finish your validation. I'll show it in two for readability:
def testStoreCreditAmountText = '-$100.00'
TestObject storeCreditTO = findTestObject('Baseline/Page_Side Menu/Page_Customers/Page_Customer Card/span_Verify Credit Limit')
def actualStoreCreditAmountText = WebUI.getText(storeCreditTO)
WebUI.verifyMatch(testStoreCreditAmountText, actualStoreCreditAmountText, false)
I hope that helps!

plone.formwidget - Is it possible to set a MasterSelect Field as an AutocompleteFieldWidget?

I am trying to set a MasterSelect field to an AutocompleteFieldWidget.
I'm using AutocompleteFieldWidget from plone.formwidget.autocomplete and the MasterSelectField from plone.formwidget.MasterSelect. The slave field belonging to the MasterSelectField is also a MasterSelectField.
The autocomplete functions as it should (retrieving the values based on input), but the slave field's choices do not change. However, when its not set as an autocomplete, everything works as it should.
Edit:
In my buildout-cache, I looked at widget.py in plone.formwidget.masterselect and tried placing a print statement in getSlaves and that function wasn't getting called. I tried the render function and that wasn't getting called either. Then I placed a print statement in MasterSelectField and that was notgetting called. Setting the field to an Autocomplete widget removes any trace that its a Master Select field.
Edit: In the init.py file in plone.formwidget.masterselect, I placed a print statement in the init function of the MasterSelectField, and the slave widget does print, where as in getSlaves in widget.py it doesn't. This is the output I'm getting from printing in the init and what I should be getting in getSlaves:
({'action': 'vocabulary', 'masterID': 'form-widgets-IMyForm-master_field',
'control_param': 'master_value', 'name': 'IMyForm.slave_field',
'vocab_method': <class 'my.product.vocabulary.SlaveVocab'>},)
I have my interface:
from plone.directives import form
class IMyForm(model.Schema):
form.widget(master_field=AutocompleteFieldWidget)
master_field = MasterSelectField(
title=_(u'Master'),
slave_fields=({'name':'IMyForm.slave_field',
'action':'vocabulary',
'source':MySource,
'control_param':'master_value'
}),
required=True,
)
slave_field = MasterSelectField(title=_(u'Slave Field'),
source=SlaveVocab,
slave_fields=(....
)
required=False,
)
I have my source object for the master field:
class MySource(object):
implements(IQuerySource)
def __init__(self, context):
simple_terms = []
#Query portal catalog for unique indexes, and fill with simple terms
self.vocab = SimpleVocabulary(simple_terms)
def __contains__(self, term):
return self.vocab.__contains__(term)
def getTermByToken(self, token):
return self.getTermByToken(token)
def getTerm(self, value):
return self.getTerm(value)
def search(self, query_string):
return [term for term in self.vocab if query_string in term.title.lower()]
class MySourceBinder(object):
implements(IContextSourceBinder)
def __call__(self, context):
return MySource(context)
My slave field's source is:
class SlaveVocab(object):
grok.implements(IContextSourceBinder)
def __init__(self, **kw):
self.master_value = kw.get('master_value', None)
def __call__(self, context):
if self.master_value is None or self.master_value == "--NOVALUE--"
self.master_value = getattr(context,'master_field',None)
#Still nothing, return empty vocabulary
if self.master_value is None or self.master_value == '--NOVALUE--':
return SimpleVocabulary([])
terms = []
#If not null, building a simple vocabulary to return
return SimpleVocabulary(terms)
I did a print statement in call of the Slave Vocabulary and it was being called, but nothing was being passed in.
I also tried using another widget, ChosenFieldWidget. I get the same results in that it functions as it should, but the slave field's choices do not change. Is it possible to set a master select field to an autocomplete? If so, what am I doing wrong?
Also, I'm using Solgema.fullcalendar and the content type extends the IEventBasic behavior, so I don't have access to using my own form class I would've liked to have used since Solgema seems to render its own forms.
Edit:
I am using Plone 4.3

flask redirect from closure

def check_login(func):
"""Check if user is logged in."""
def decorator(*args, **kwargs):
if not login_session_test():
print ("Not logged in - redirect to /login")
flash ("Well that was wrong. Chicken winner. No more dinner.")
return redirect(url_for('login'))
print ("Logged in, do what needs to be done.")
return func(*args, **kwargs)
return decorator
#check_login
#app.route("/sacred/secret/stuff", methods=['GET'])
def funfunfun():
return "Super fun"
It never redirects to /login but gives some garbage like page.
Swapping the #/closure order yields:
AssertionError: View function mapping is overwriting an existing endpoint function: decorator
I am not yet fully pythonized.
Your decorator order is incorrect, and you are not copying across the function name to the wrapper function.
Use this order:
#app.route("/sacred/secret/stuff", methods=['GET'])
#check_login
def funfunfun():
return "Super fun"
Otherwise the undecorated function is registered for the view.
Use #functools.wraps() to have various pieces of metadata copied over from the original wrapped function to the wrapper that replaces it:
from functools import wraps
def check_login(func):
"""Check if user is logged in."""
#wraps(func)
def decorator(*args, **kwargs):
if not login_session_test():
print ("Not logged in - redirect to /login")
flash ("Well that was wrong. Chicken winner. No more dinner.")
return redirect(url_for('login'))
print ("Logged in, do what needs to be done.")
return func(*args, **kwargs)
return decorator
Routes need an endpoint name, and if you don't specify one explicitly, Flask uses the name of the function (from functionobj.__name__). But your decorator wrapper object has the name decorator, so if you use the decorator more than once Flask complains that it already has used that endpoint name.
#functools.wraps() copies across the __name__ attribute, so now your decorator wrapper is also called funfunfun, whereas another decorated route function gets to keep its name too.

Modifying a variable class attribute

I'm trying to modify a class attribute based on the argument given. I'm just getting into python but I can't seem to find a way to do it without using a dictionary. Is there a pythonic way to do this? See example below
class Ship:
def __init__(self, name):
self.name = name
ship_type = {"schooner": [50, 30, 18],
"galleon": [30, 14, 14]
}
self.max_weight = ship_type[name][0]
self.speed = ship_type[name][1]
self.poopdeck = ship_type[name][2]
def upgrade(self, attribute, value):
self.attribute += value
Someship.ship.upgrade(speed, 10)
I can write out a different method for each attribute but I feel as if there has to be something like this.
I apologize in advance if this has already been answered but I couldn't word it right if there is.
Change the update method to update an existing attribute by using the builtin functions hasattr(), setattr() and getattr().
def upgrade(self, attribute, value):
if hasattr(self, attribute):
setattr(self, attribute, getattr(self, attribute) + value )
else:
raise AttributeError("Can't upgrade non-existent attribute '{}'.".format(attribute))
Note that I'd also use the __dict__ attribute to make setting up your instances easier:
class Ship:
# types is a class variable, and will be the same for all instances,
# and can be referred to by using the class. ie `Ship.types`
types = {
"schooner": {'weight':50, 'speed':30, 'poopdeck':18},
"galleon": {'weight':30, 'speed':14, 'poopdeck':14},
"default": {'weight':11, 'speed':11, 'poopdeck':11}
}
def __init__(self, name):
self.name = name
# we update the instance dictionary with values from the class description of ships
# this means that instance.speed will now be set, for example.
if name in Ship.types:
self.__dict__.update(Ship.types[name])
else:
self.__dict__.update(Ship.types["default"])
def upgrade(self, attribute, value):
if hasattr(self, attribute):
setattr(self, attribute, getattr(self, attribute) + value )
else:
raise AttributeError("Can't upgrade non-existent attribute '{}'.".format(attribute))
ship = Ship("schooner")
print(ship.speed) #=> 30
ship.upgrade("speed", 10)
print(ship.speed) #=> 40
You are looking for the setattr and getattr functions. Your upgrade method can be implemented as
def upgrade(self, attribute, value):
setattr(self, attribute, getattr(self, attribute) + value )

Django-Nonrel with Mongodb listfield

I am trying to implement manytomany field relation in django-nonrel on mongodb. It was suggessted at to:
Django-nonrel form field for ListField
Following the accepted answer
models.py
class MyClass(models.Model):
field = ListField(models.ForeignKey(AnotherClass))
i am not sure where the following goes, it has been tested in fields.py, widgets,py, models.py
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(SelectMultiple):
pass
class FormListField(MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
admin.py
class MyClassAdmin(admin.ModelAdmin):
form = MyClassForm
def __init__(self, model, admin_site):
super(MyClassAdmin,self).__init__(model, admin_site)
admin.site.register(MyClass, MyClassAdmin)
The following Errors keep popping up:
If the middle custom class code is used in models.py
name 'SelectMultiple' is not defined
If custom class code is taken off models.py:
No form field implemented for <class 'djangotoolbox.fields.ListField'>
You just need to import SelectMultiple by the sound of it. You can put the code in any of those three files, fields.py would make sense.
Since it's pretty usual to have:
from django import forms
at the top of your file already, you probably just want to edit the code below to:
# you'll have to work out how to import the Mongo ListField for yourself :)
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(forms.SelectMultiple):
pass
class FormListField(forms.MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
You probably also want to try and learn a bit more about how python works, how to import modules etc.