WTForms custom validator- RequiredIf on a RadioField - validation

I borrowed a validator that requires the user to input data if the value of another field is a certain value:
class RequiredIf(object):
def __init__(self, *args, **kwargs):
self.conditions = kwargs
def __call__(self, form, field):
for name, data in self.conditions.items():
if name not in form._fields:
Optional(form, field)
else:
condition_field = form._fields.get(name)
if condition_field.data == data and not field.data:
DataRequired()(form, field)
Optional()(form, field)
This works really well when the field containing the validator argument is a TextField, but it doesn't seem to work when the field is a RadioField. How can I adapt the validator so that this also works on RadioFields?
As it stands, regardless of whether the validation condition applies or not, not a valid choice is always returned for the RadioField.
Thanks in advance.
For example:
class new_form(Form):
code=BooleanField('Do you code?')
code2=RadioField('If so, what languages do you use?',
choices=[('python','python'),('C++','C++')],
validators=[RequiredIf(code=1)])
Regardless of whether the BooleanField code is checked or not, this is not a valid choice is always returned for code2. I would like a validator that requires an input for any type of field(including RadioField), conditional on the value of another field (code=1 in this case).

Updated!. You can create any custom processing using __call__. Example:
from multidict import CIMultiDict
from wtforms import Form, RadioField, BooleanField
class RequiredIf(object):
def __init__(self, **kwargs):
self.conditions = kwargs
def __call__(self, form, field):
# NOTE! you can create here any custom processing
current_value = form.data.get(field.name)
if current_value == 'None':
for condition_field, reserved_value in self.conditions.items():
dependent_value = form.data.get(condition_field)
if condition_field not in form.data:
continue
elif dependent_value == reserved_value:
# just an example of error
raise Exception(
'Invalid value of field "%s". Field is required when %s==%s' % (
field.name,
condition_field,
dependent_value
))
class NewForm(Form):
code = BooleanField('Do you code?')
code2 = RadioField(
'If so, what languages do you use?',
choices=[('python', 'python'), ('C++', 'C++')],
validators=[RequiredIf(code=True)])
form = NewForm(formdata=CIMultiDict(code=True, code2='python'), )
form.validate() # valid data - without errors
# invalid data
form = NewForm(formdata=CIMultiDict(code=True), )
form.validate() # invalid data - Exception: Invalid value of field "code2". Field is required when code==True
One more example with 2 RadioField:
class NewForm(Form):
list_one = RadioField('City/Country', choices=['city', 'country'])
list_two = RadioField(
'Cities',
choices=[('minsk', 'Minsk'), ('tbilisi', 'Tbilisi')],
validators=[RequiredIf(list_one='city')])
form = NewForm(formdata=CIMultiDict(list_one='city', list_two='minsk'), )
form.validate() # without errors
form = NewForm(formdata=CIMultiDict(list_one='country'), )
form.validate() # without errors
form = NewForm(formdata=CIMultiDict(list_one='city'), )
form.validate() # invalid data - Exception: Invalid value of field "list_two". Field is required when list_one==city
Hope this helps.

Related

Drop Mime data (csv file) into QTableView with custom QSqlTableModel is not working

I'm using a QTableView along with a superclassed QSqlTableModel to display a sqlite table into Qt and inserting new records dropping a csv file.
I have followed the doc and came up with the example reported below. I have recreated a very light reproducible example to show what is happening to my code, no sanity check or quality code has been intentionally used. It's tested against PySide6
import sys
from qtpy.QtWidgets import QApplication, QTableView, QWidget
from qtpy.QtCore import QModelIndex, QMimeData, Qt
from qtpy.QtSql import QSqlDatabase, QSqlTableModel, QSqlQuery
from pandas import read_csv
def create_table():
# Dummy very simple table
_query_str = """CREATE TABLE MyTable (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
Field1 INTEGER,
Field2 TEXT);"""
query = QSqlQuery(db=db, query=_query_str)
query.exec_()
class MyTableModel(QSqlTableModel):
def __init__(self, table_name, db):
QSqlTableModel.__init__(self, db=db)
self.setTable(table_name)
def canDropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool:
return True # <-- Just for the example
def supportedDropActions(self) -> Qt.DropAction:
return Qt.DropAction.CopyAction | Qt.DropAction.MoveAction | Qt.DropAction.LinkAction
def dropMimeData(self, data: QMimeData, action: Qt.DropAction, row: int, column: int, parent: QModelIndex) -> bool:
csv_filename = data.urls()[0].toLocalFile()
df = read_csv(csv_filename, delimiter=',', header=0)
for _, row in df.iterrows():
record = self.record()
record.remove(0) # <-- Remove the ID field
record.setValue('Field1', row['Field1'].values[0])
record.setValue('Field2', row['Field2'].values[0])
self.insertRecord(-1, record)
if __name__ == '__main__':
# In memory database just for the purpose of the example
db = QSqlDatabase.addDatabase("QSQLITE", ":memory:")
db.open()
if not db.open():
raise "Database not opened"
create_table()
app = QApplication([])
table = QTableView()
model = MyTableModel('MyTable', db)
table.setModel(model)
table.setAcceptDrops(True)
table.show()
sys.exit(app.exec_())
What I get is that canDropMimeData and supportedDropActions are correctly called, but (using debug) dropMimeData is never called
And the below image shows that, even if canDropMimeData returns True, the file seems not to be accepted.
Edit 1 - QSqlTableModel issue
I found out that the problem is with QSqlTableModel. If I use a bare QStandardItemModel, everything works fine. Any work-around?
By default, item models don't provide drag and drop support.
In order to properly allow that, many aspects have to be checked, including that the flags() returned by any index that would accept drop must also have the Qt.ItemIsDropEnabled.
If you want to allow that only for the model (drop on an empty area, not on items), that index would be the root index, aka, an invalid index:
def flags(self, index):
flags = super().flags(index)
if not index.isValid():
flags |= Qt.ItemIsDropEnabled
return flags

TypeError: Object of type RowProxy is not JSON serializable - Flask

I am using SQLAlchemy to query the database from my Flask web-application using engine.After I do the SELECT Query and also do use fetchall object after ResultProxy is returned which ultimately returns RowProxy object and then I store in session.
Here is my code:
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from flask import Flask, session
engine = create_engine(os.environ.get('DATABASE_URL'))
db = scoped_session(sessionmaker(bind=engine))
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY')
#app.route('/')
def index():
session['list'] = db.execute("SELECT title,author,year FROM books WHERE year = 2011 LIMIT 4").fetchall()
print(session['list'])
return "<h1>hello world</h1>"
if __name__ == "__main__":
app.run(debug = True)
Here is the output:
[('Steve Jobs', 'Walter Isaacson', 2011), ('Legend', 'Marie Lu', 2011), ('Hit List', 'Laurell K. Hamilton', 2011), ('Born at Midnight', 'C.C. Hunter', 2011)]
Traceback (most recent call last):
File "C:\Users\avise\AppData\Local\Programs\Python\Python38\Lib\site-packages\flask\app.py", line 2463, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\avise\AppData\Local\Programs\Python\Python38\Lib\site-packages\flask\app.py", line 2449, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\avise\AppData\Local\Programs\Python\Python38\Lib\site-packages\flask\app.py", line 1866, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\avise\AppData\Local\Programs\Python\Python38\Lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type RowProxy is not JSON serializable
The session item stores the data as i can see in output.But "hello world" is not rendered.
And if i replace the session variable by ordinary variable say x then it seems to be working.
But i think i need to use sessions so that my application will be used simultaneously by to users to display different things. So, how could i use sessions in this case or is there any other way?
Any help will be appreciated as I am new to Flask and web-development.
From what I understand about the Flask Session object is that it acts as a python dictionary; however values must be JSON serializable. In this case, just like the error suggests, the RowProxy object that is being returned by fetch all is not json serializable.
A solution to this problem would be to instead pass through a result of your query as a dictionary (which is JSON serializable).
It looks like the result of your query is returning a list of tuples so we can do the following:
res = db.execute("SELECT title,author,year FROM books WHERE year = 2011 LIMIT 4").fetchall()
user_books = {}
index = 0
for entry in res:
user_books[index] = {'title':res[index][0],
'author':res[index][1],
'year':res[index][2],
}
index += 1
session['list'] = user_books
A word of caution; however, is that since we are using the title of the book as a key, if there are two books with the same title, information may be overwritten, so consider using a unique id as the key.
Also note that the dictionary construction above would only work for the query you already have - if you added another column to the select statement you would have to edit the code to include the extra column information.

How to have state in a an ansible callback plugin

I want to make a ansible callback plugin, that hides sensitive data in the ansible output. There is a suggestion on how to do it here:
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
import os, collections
class CallbackModule(CallbackModule_default):
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'protect_data'
def __init__(self, display=None):
super(CallbackModule, self).__init__(display)
def hide_password(self, result):
ret = {}
for key, value in result.iteritems():
if isinstance(value, collections.Mapping):
ret[key] = self.hide_password(value)
else:
if "password" in key:
ret[key] = "********"
else:
ret[key] = value
return ret
def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):
return super(CallbackModule, self)._dump_results(self.hide_password(result), indent, sort_keys, keep_invocation)
Now this example hides "password". I now want to make the word, that are hidden configurable at runtime of the playbook.
Can I somehow give the plugin a state (a list of words to hide) and modify it at the runtime of the playbook?
You can set self.words_list inside __init__ to some default value.
Then inside ...on_task_start and ...on_handler_task_start check for some specific variable and modify your self.words_list accordingly.
You can take a look at how persistent properties to collect statistics are used in profile_tasks callback plugin.

Displaying custom tags in YRI

I've created some custom tags in my .yardopts file that look like this:
--name-tag optfield:"Field that is not required to be filled out by the user."
--name-tag nonfield:"Parameter that is not a field to be filled out by the user."
They are being displayed quite nicely when I view them in a web-browser, but when I try to view a class with yri I don't see my custom tags being displayed.
For example, this class
#This is a boring class
class Boring
# This function is the index.
# #param required_field [String] Required!
# #optfield optional_argument [String] Totally not required dude.
# #nonfield webData [WebData] Not even an option.
def index(optional_argument = "", webData = $wedData)
end
end
Get displayed as this in yri when I run yri Boring#index
------------------------------------------------ Method: #index (Boring)
(Defined in: YardTest.rb)
boring.index(optional_argument = "", webData = $wedData) -> Object
------------------------------------------------------------------------
This function is the index.
Parameters:
-----------
(String) required_field - Required!
Is there a way to configure yri to display my custom tags?

Django Form Preview - Adding the User to the Form before save

class RegistrationFormPreview(FormPreview):
preview_template = 'workshops/workshop_register_preview.html'
form_template = 'workshops/workshop_register_form.html'
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
# data = request.POST.copy()
# data['user_id'] = u'%s' % (request.user.id)
# cleaned_data['user'] = u'%s' % (request.user.id)
#f = self.form(cleaned_data)
#f = self.form(data)
#f.user = request.user
f = self.form(request.POST)
f.save()
pdb.set_trace()
return HttpResponseRedirect('/register/success')
As you can see, I've tried a few ways, and that have been commented out. The task is apparently simple: Add the user from request to the form before save, and then save.
What is the accepted, working method here?
If the user can't be modified, I would say it shouldn't even be included in the form in the first place.
Either way, using the commit argument to prevent the resulting object being saved immediately should work (assuming FormPreview uses ModelForm):
obj = form.save(commit=False)
obj.user = request.user
obj.save()

Resources