Return elements in SQLAlchemy AppenderBaseQuery object to Flask-WTForm field - flask-sqlalchemy

I am working on the "edit" functionality for a form. When the form is rendered, the fields should populate with data that is stored in the database. One of this field is returned as a SQlAlchemy AppenderBaseQuery object. This causes a query statement to be pre-populated in the field, instead of the results of the query.
How do I pre-populate the form with elements in the object instead?
form = MyForm(obj=MyObject)
if form.validate_on_submit():
form.populate_obj(MyObject)
The field is defined as a StringField.
Note:
I am passing MyObject to pre-populate the form because there is a thing with WTForm where form.Field.data does not get updated at all.
form = MyForm()
form.Field.data = "test" #Pre-populate Field with "test"
if validate_on_submit():
MyObject.Field = Form.Field.data #Assign data in Field to MyObject
#...
My Object.Field will not be updated with the new Form.Field.data, not sure why.

As suggested by n-Holmes in the comments:
# Do not initiate the form with object
form = MyForm()
# Split logic to 'GET' and 'POST request'
if request.method == 'GET'
MyForm.Field.Data = ', '.join([element.attribute for element in MyForm.Field.all()]
# MyForm.Field is an AppenderBaseQuery Object >> Returns a query statement
# MyForm.Field.all() returns a list of Objects
# The list comprehension returns a list of a specific attribute for each object
# The .join combines the list to a string, separated by ", "
# This entire thing is pre-populated onto the form
# Eg: "Attribute01, Attribute02, Attribute03"
if validate_on_submit:
MyObject.Field = FunctionToUpdateField(form.Field.data)
# form.Field.data will be a string
# FunctionToUpdateField compares above string with what is stored in db ...
# determines which elements to add / remove

Related

How to input via gradio dropdowns using variable information stored in a data frame?

I'm trying to input info from an easily editable CSV file where the first row contains the variable names. I am doing this by iterating over the list of variables so each variable is a new dropdown. Using this method everything appears correctly in the interface but something is going wrong when it comes to outputting the selected values. I'm thinking I need another approach but I can't think how to do it short of generating a new gradio interface for each dropdown.
the following code hangs when you click submit:
import gradio as gr
import pandas as pd
df = pd.read_csv(file_path)
# first row of the CSV as the variable names
variables = list(df.columns)
# filter out null values
df = df.dropna()
# rest of the rows as the options for each variable
options = {var: list(df[var]) for var in variables}
def assign_values(**kwargs):
global save_variables
save_variables.update(kwargs)
return kwargs
save_variables = {}
inputs = [gr.inputs.Dropdown(label=var, choices=options[var], type="value") for var in variables]
outputs = ["text"]
gr.Interface(fn=assign_values, inputs=inputs, outputs=outputs, title="Assign Values").launch()

adding/removing objects to a list in vb.net

i have a list of objects called gobletinv that i want to add and remove objects from
right now to add a new object I've just done this
gobletinv(gobletpointer).mainstattype = MST.Text
gobletinv(gobletpointer).mainstatvalue = MSV.Text
gobletinv(gobletpointer).substat1type = ST1.Text
gobletinv(gobletpointer).substat1value = SV1.Text
gobletinv(gobletpointer).substat2type = ST2.Text
gobletinv(gobletpointer).substat2value = SV2.Text
gobletinv(gobletpointer).substat3type = ST3.Text
gobletinv(gobletpointer).substat3value = SV3.Text
gobletinv(gobletpointer).substat4type = ST4.Text
gobletinv(gobletpointer).substat4value = SV4.Text
gobletpointer += 1
i currently have no idea how i would remove an object from this list
Let's assume that the type your collection holds is called Stat. Let's also assume that gobletpointer is an Integer with an initial value of 0.
Each line where you are referencing the collection, you start it off with:
gobletinv(gobletpointer)
What this does is get the item from the collection at a given index.
So right now when you set the various property values to their respective TextBox value, you are overwriting the existing item in the collection.
If you wanted to add a new item to the collection, you would use the Add method (documentation). For example:
gobletinv.Add(New Stat() With {
mainstattype = MST.Text,
mainstatvalue = MSV.Text
substat1type = ST1.Text,
substat1value = SV1.Text,
substat2type = ST2.Text,
substat2value = SV2.Text,
substat3type = ST3.Text,
substat3value = SV3.Text,
substat4type = ST4.Text,
substat4value = SV4.Text
})
Now if you wanted to remove the object from the collection, it depends on how you want to remove it. But here is an example of leveraging the RemoveAt method (documentation) to remove the first record from the collection:
gobletinv.RemoveAt(0)
Update: This is a fiddle demonstrating how to add/remove items to the collection. https://dotnetfiddle.net/c0W6yS

How to correct filter fields domain in a model?

I'm getting the following error trying to filter a field by another field value of the same model.
File
"/opt/..../tfutbol/models/partido.py",
line 29, in Partido
figura = fields.Many2one('tfutbol.jugador',domain=[('equipo_id','=',local.id)])
RuntimeError: maximum recursion depth exceeded while calling a Python
object
The line of code trying with the problem is:
figura = fields.Many2one('tfutbol.jugador',domain=[('equipo_id','=',local.id),('equipo_id','=',visitante.id)])
All the relevant code, is above:
class Partido(models.Model):
_name = 'tfutbol.partido'
local = fields.Many2one('tfutbol.equipo')
visitante = fields.Many2one('tfutbol.equipo')
figura = fields.Many2one('tfutbol.jugador',domain=[('equipo_id','=',local.id),('equipo_id','=',visitante.id)])
class Equipo(models.Model):
_name = 'tfutbol.equipo'
name = fields.Char('Nombre')
jugador_ids = fields.One2many('tfutbol.jugador', 'equipo_id', string="Jugadores")
class Jugador(models.Model):
_name = 'tfutbol.jugador'
name = fields.Char('Nombre')
equipo_id = fields.Many2one('tfutbol.equipo')
Thanks for reading!
If you read the docstring on /odoo/fields.py on the Many2one class definition you will see that:
:param domain: an optional domain to set on candidate values on the
client side (domain or string)
That means that you cannot use the dot notation (record.field) to pull in values because this has not been implemented on javascript.
So what you can do to dynamically create a domain like the one you want is:
Create an onchange method that will be invoked every time you set a value on the local and visitante fields and will change the domain on the figura field. For example:
#api.onchange('figura','visitante')
def onchange_method(self):
domain = {}
domain['figura'] = [('equipo_id','=',self.local.id),('equipo_id','=',self.visitante.id)]
return {'domain': domain}
Note: When you do not set an operator in a domain with multiple leafs (parameters) an explicit AND is added, so in your domain you are searching for the equipo_id filed to be equal to self.local.id AND self.visitante.id which will not work, you might want to add an OR like:
['|',('equipo_id','=',self.local.id),('equipo_id','=',self.visitante.id)]

Odoo: Access field by it's name (given as string)

I have a model, where I want to access a field, given by a string. Example:
def test(self):
field = 'name'
name = getattr(self, field)
This works fine - name is set to self.name. But then I want to access a related field:
def test2(self):
field = 'partner_id.name'
name = getattr(self, field)
That doesn't work (because 'partner_id.name' does not exist on self). Any idea how to do it right?
getattr doesn't support the dot notation, only simple attribute names. You can however create a simple function that does:
def getfield(model, field_name):
value = model
for part in field_name.split('.'):
value = getattr(value, part)
return value
You would use it like this:
def test2(self):
field = 'partner_id.name'
name = getfield(self, field)
You need to use the object that contain partner_id.name
def test2(self):
field = 'name'
object = self.pool.get('res.partner').browse(cr, uid, self.partner_id.id)#v7
#object = self.env['res.partner'].browse(self.partner_id.id)#v8
name = getattr(object, field)
I also came across another solution, inspired by the mail template system:
from openerp.tools.safe_eval import safe_eval as eval
def test2(self):
field = 'partner_id.name'
field = 'object.' + field
name = eval(field, {'object': self})

How do I loop thought each DB field to see if range is correct

I have this response in soapUI:
<pointsCriteria>
<calculatorLabel>Have you registered for inContact, signed up for marketing news from FNB/RMB Private Bank, updated your contact details and chosen to receive your statements</calculatorLabel>
<description>Be registered for inContact, allow us to communicate with you (i.e. update your marketing consent to 'Yes'), receive your statements via email and keep your contact information up to date</description>
<grades>
<points>0</points>
<value>No</value>
</grades>
<grades>
<points>1000</points>
<value>Yes</value>
</grades>
<label>Marketing consent given and Online Contact details updated in last 12 months</label>
<name>c21_mrktng_cnsnt_cntct_cmb_point</name>
</pointsCriteria>
There are many many many pointsCriteria and I use the below xquery to give me the DB value and Range of what that field is meant to be:
<return>
{
for $x in //pointsCriteria
return <DBRange>
<db>{data($x/name/text())}</db>
<points>{data($x//points/text())}</points>
</DBRange>
}
</return>
And i get the below response
<return><DBRange><db>c21_mrktng_cnsnt_cntct_cmb_point</db><points>0 1000</points></DBRange>
That last bit sits in a property transfer. I need SQL to bring back all rows where that DB field is not in that points range (field can only be 0 or 1000 in this case), my problem is I dont know how to loop through each DBRange/DBrange in this manner? please help
I'm not sure that I really understand your question, however I think that you want to make queries in your DB using specific table with a column name defined in your <db> field of your xml, and using as values the values defined in <points> field of the same xml.
So you can try using a groovy TestStep, first parse your Xml and get back your column name, and your points. To iterate over points if the values are separated with a blank space you can make a split(" ") to get a list and then use each() to iterate over the points on this list. Then using groovy.sql.Sql you can perform the queries in your DB.
Only one more thing, you need to put the JDBC drivers for your vendor DB in $SOAPUI_HOME/bin/ext and then restart SOAPUI in order that it can load the necessary driver classes.
So the follow code approach can achieve your goal:
import groovy.sql.Sql
import groovy.util.XmlSlurper
// soapui groovy testStep requires that first register your
// db vendor drivers, as example I use oracle drivers...
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver( "oracle.jdbc.driver.OracleDriver")
// connection properties db (example for oracle data base)
def db = [
url : 'jdbc:oracle:thin:#db_host:d_bport/db_name',
username : 'yourUser',
password : '********',
driver : 'oracle.jdbc.driver.OracleDriver'
]
// create the db instance
def sql = Sql.newInstance("${db.url}", "${db.username}", "${db.password}","${db.driver}")
def result = '''<return>
<DBRange>
<db>c21_mrktng_cnsnt_cntct_cmb_point</db>
<points>0 1000</points>
</DBRange>
</return>'''
def resXml = new XmlSlurper().parseText(result)
// get the field
def field = resXml.DBRange.db.text()
// get the points
def points = resXml.DBRange.points.text()
// points are separated by blank space,
// so split to get an array with the points
def pointList = points.split(" ")
// for each point make your query
pointList.each {
def sqlResult = sql.rows "select * from your_table where ${field} = ?",[it]
log.info sqlResult
}
sql.close();
Hope this helps,
Thanks again for your help #albciff, I had to add this into a multidimensional array (I renamed field to column and result is a large return from the Xquery above)
def resXml = new XmlSlurper().parseText(result)
//get the columns and points ranges
def Column = resXml.DBRange.db*.text()
def Points = resXml.DBRange.points*.text()
//sorting it all out into a multidimensional array (index per index)
count = 0
bigList = Column.collect
{
[it, Points[count++]]
}
//iterating through the array
bigList.each
{//creating two smaller lists and making it readable for sql part later
def column = it[0]
def points = it[1]
//further splitting the points to test each
pointList = points.split(" ")
pointList.each
{//test each points range per column
def sqlResult = sql.rows "select * from my_table where ${column} <> ",[it]
log.info sqlResult
}
}
sql.close();
return;