JSON Grail Groovy Update SQL - sql

Using a Groovy script with grails and want to do an update to a record in the database. I do the basic get the object from JSON and convert it to the Domain class and then do save() on it. From what I understand since I am new to Groovy and grails the save should update if the "id" is already there. But I don't get that, I get the standard SQL error of "Duplicate entry '1' for key 'PRIMARY'". How do I fix this?
def input = request.JSON
def instance = new Recorders(input)
instance.id = input.getAt("id")
instance.save()
and my domain is:
class Recorders {
Integer sdMode
Integer gsmMode
static mapping = {
id generator: "assigned"
}
static constraints = {
sdMode nullable: true
gsmMode nullable: true
}
}

Instead of doing a new Recorders(input), you probably ought to get it:
def input = request.JSON
def instance = Recorders.get(input.getAt('id'))
instance.properties = input
instance.save()
Edit
(From your comment) If it doesn't exist and you want to insert it:
def input = request.JSON
def id = input.getAt('id')
def instance = Recorders.get(id)
if(!instance) {
instance = new Recorders(id: id)
}
instance.properties = input
instance.save()
I don't use assigned id generators much, so I'm not sure if Grails will bind the id automatically (since it's expecting it to be assigned). If it does, you can probably remove the id: id from the Recorders() constructor.

Related

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})

automated mapping in groovy for ODI with expression

this is my first question and I hope you can help me. I make a script in Groovy (in Oracle Data Integrator 12c) to automate mappings. Here is the description of my prodecure:
1 step: removing old mapping if exists.
2 step: looking for the project and the folder (if doesn't exist: create new one).
3 step: create new mapping
4 step: implement source and target table
5 step: create expression
6 step: link every column
Now my question: Can someone help me to make this script with a dynamic expression? Like this:
step 1: get the data types of the target columns
step 2: get the right data types into the expression
step 3: change the false types (always Varchar) into the right types (Number or Date or still Varchar)
step 4: link every column
My handicap: I have never done something with groovy and in Java I'm not very good. So it is not possible for me to make this dynamic. Almost everything in my Script is placed together from some internet sites. It would be great to find some guys who know something about my problem. And I think it would be a good script for all who will change from OWB to ODI.
Thanks!
//Von ODI Studio erstellt
//
//name of the project
projectName = "SRC_TO_TRG"
//name of the folder
ordnerName = "FEN_TEST"
//name of the mapping
mappingName = "MAP1_FF_TO_TRG"
//name of the model
modelName = "DB_FEN"
//name of the source datastore
sourceDatastoreName = "SRC_TEST_FEN"
//name of the target datastore
targetDatastoreName = "TRG_TEST_FEN"
import oracle.odi.domain.project.finder.IOdiProjectFinder
import oracle.odi.domain.model.finder.IOdiDataStoreFinder
import oracle.odi.domain.project.finder.IOdiFolderFinder
import oracle.odi.domain.project.finder.IOdiKMFinder
import oracle.odi.domain.mapping.finder.IMappingFinder
import oracle.odi.domain.adapter.project.IKnowledgeModule.ProcessingType
import oracle.odi.domain.model.OdiDataStore
import oracle.odi.core.persistence.transaction.support.DefaultTransactionDefinition
//set expression to the component
def createExp(comp, tgtTable, propertyName, expressionText) {
DatastoreComponent.findAttributeForColumn(comp,tgtTable.getColumn(propertyName)) .setExpressionText(expressionText)
}
//delete mapping with the same name
def removeMapping(folder, map_name) {
txnDef = new DefaultTransactionDefinition()
tm = odiInstance.getTransactionManager()
tme = odiInstance.getTransactionalEntityManager()
txnStatus = tm.getTransaction(txnDef)
try {
Mapping map = ((IMappingFinder) tme.getFinder(Mapping.class)).findByName(folder, map_name)
if (map != null) {
odiInstance.getTransactionalEntityManager().remove(map);
}
} catch (Exception e) {e.printStackTrace();}
tm.commit(txnStatus)
}
//looking for a project and folder
def find_folder(project_code, folder_name) {
txnDef = new DefaultTransactionDefinition()
tm = odiInstance.getTransactionManager()
tme = odiInstance.getTransactionalEntityManager()
txnStatus = tm.getTransaction(txnDef)
pf = (IOdiProjectFinder)tme.getFinder(OdiProject.class)
ff = (IOdiFolderFinder)tme.getFinder(OdiFolder.class)
project = pf.findByCode(project_code)
//if there is no project, create new one
if (project == null) {
project = new OdiProject(project_code, project_code)
tme.persist(project)
}
//if there is no folder, create new one
folderColl = ff.findByName(folder_name, project_code)
OdiFolder folder = null
if (folderColl.size() == 1)
folder = folderColl.iterator().next()
if (folder == null) {
folder = new OdiFolder(project, folder_name)
tme.persist(folder)
}
tm.commit(txnStatus)
return folder
}
//name of the project and the folder
folder = find_folder(projectName,ordnerName)
//delete old mapping
removeMapping(folder, mappingName)
txnDef = new DefaultTransactionDefinition()
tm = odiInstance.getTransactionManager()
tme = odiInstance.getTransactionalEntityManager()
txnStatus = tm.getTransaction(txnDef)
dsf = (IOdiDataStoreFinder)tme.getFinder(OdiDataStore.class)
mapf = (IMappingFinder) tme.getFinder(Mapping.class)
//create new mapping
map = new Mapping(mappingName, folder);
tme.persist(map)
//insert source table
boundTo_emp = dsf.findByName(sourceDatastoreName, modelName)
comp_emp = new DatastoreComponent(map, boundTo_emp)
//insert target table
boundTo_tgtemp = dsf.findByName(targetDatastoreName, modelName)
comp_tgtemp = new DatastoreComponent(map, boundTo_tgtemp)
//create expression-operator
comp_expression = new ExpressionComponent(map, "EXPRESSION")
// define expression
comp_expression.addExpression("LAND_KM", "TO_NUMBER(SRC_TEST_FEN.LAND_KM)", null,null,null);
comp_expression.addExpression("DATE_OF_ELECTION", "TO_DATE(SRC_TEST_FEN.DATE_OF_ELECTION, 'DD.MM.YYYY')", null,null,null);
//weitere Transformationen anhängen möglich
//link source table with expression
comp_emp.connectTo(comp_expression)
//link expression with target table
comp_expression.connectTo(comp_tgtemp)
createExp(comp_tgtemp, boundTo_tgtemp, "ABBR", "SRC_TEST_FEN.ABBR")
createExp(comp_tgtemp, boundTo_tgtemp, "NAME", "SRC_TEST_FEN.NAME")
createExp(comp_tgtemp, boundTo_tgtemp, "LAND_KM", "EXPRESSION.LAND_KM")
createExp(comp_tgtemp, boundTo_tgtemp, "DATE_OF_ELECTION", "EXPRESSION.DATE_OF_ELECTION")
tme.persist(map)
tm.commit(txnStatus)
You can pass the Datatype as the third argument of the method addExpression.
You can also pass the size and the scale as fourth and fifth arguments.
For instance, for the LAND_KM expression, replace your line by this :
MapAttribute map_attr = DatastoreComponent.findAttributeForColumn(comp_tgtemp,boundTo_tgtemp.getColumn("LAND_KM"))
comp_expression.addExpression("LAND_KM", "TO_NUMBER(SRC_TEST_FEN.LAND_KM)", map_attr.getDataType(),map_attr.getSize(),map_attr.getScale());
It retrieves the target column for LAND_KM thanks to findAttributeForColumn, then retrieves the datatype, the size and the scale, and use that when adding the new expression in the Expression component.
If you want to auto map it based on the name, David Allan wrote a post on the official Oracle blog about how to do it and he provides his code : https://blogs.oracle.com/dataintegration/entry/odi_12c_mapping_sdk_auto

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;

nHibernate set referenced property by id

I have the following method which is called from Ajax:
[Authorize]
[ValidateInput(false)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public JsonNetResult CreateOrUpdateTimeRecord(TimeRecord tr)
{
TimeRecord trLocal;
if (tr.Id == -1 || tr.Id == 0)
{
trLocal = new TimeRecord
{
Description = tr.Description,
StartTime = tr.StartTime,
EndTime = tr.EndTime,
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},// _userRepo.Get(tr.User.Id),
Hdt = new Hdt {Id = tr.Hdt.Id}//_hdtRepo.Get(tr.Hdt.Id)
};
_timeRepo.Insert(trLocal);
}
else
{
trLocal = _timeRepo.Get(tr.Id);
trLocal.Description = tr.Description;
trLocal.StartTime = tr.StartTime;
trLocal.EndTime = tr.EndTime;
_timeRepo.Update(trLocal);
}
...
}
As you can see my TimeRecord has a reference to User and Hdt. Now I started to work with NHibernate Profiler which complains when I resolve my properties by loading them from their coresponding repositories. Which is clear to me since I actually don't need to query the database for that since I have the ID's for this objects.
User = _userRepo.Get(tr.User.Id),
Hdt = _hdtRepo.Get(tr.Hdt.Id)
But I'm not 100% sure if I can use this instead:
User =new myTimeMvc.Models.NHibernate.Models.User {Id = tr.User.Id},,
Hdt = new Hdt {Id = tr.Hdt.Id}
I guess NHibernate lazy proxies work the same way since they only contain just the ID of the related object and load the rest when it is needed. Do I have to attach this "new" oject anyway to my session?
Can someone tell me what is the correct way to do this?
Cheers,
Stefan
There are a few ways how to achieve that. One of them could be using the Load() method. Check Ayendes post: NHibernate – The difference between Get, Load and querying by id, an extract:
Load will never return null. It will always return an entity or throw an exception. Because that is the contract that we have we it, it is permissible for Load to not hit the database when you call it, it is free to return a proxy instead.
Other words, we can do something like this
User = _userRepo.Load(tr.User.Id),
Hdt = _hdtRepo.Load(tr.Hdt.Id)
Where the Load would be encapsulating the session.Load()