grails hql left outer join - grails

how is it possible to left outer join 2 tables?
class Person {
String firstName
String lastName
String gender
//static hasMany = [votes: Vote]
static mapping = {
version false
}
static constrains = {
}
}
class Vote {
Person voter;
Person subject;
static mapping = {
version false
}
static constraints = {
voter nullable: false
subject nullable: false
}
}
i need to get every person thats not subjected to a vote, for a specific person.
lets say person 1 votes for 3 out of 5 persons, i need the other 2 that he didnt vote for to show up for him.
How is the query supposed to be?
EDIT:
def personInstance1 = new Person(firstName: "Antonio", lastName: "Vivaldi", gender: "m")
def personInstance2 = new Person(firstName: "Dennis", lastName: "Rodman", gender: "m")
def personInstance3 = new Person(firstName: "Marc", lastName: "Oh", gender: "m")
def personInstance4 = new Person(firstName: "Gudrun", lastName: "Graublume", gender: "w")
def personInstance5 = new Person(firstName: "Hilde", lastName: "Feuerhorn", gender: "w")
def personInstance6 = new Person(firstName: "Mandy", lastName: "Muller", gender: "w")
personInstance1.save()
personInstance2.save()
personInstance3.save()
personInstance4.save()
personInstance5.save()
personInstance6.save()
def voteInstance1 = new Vote(voter: personInstance1, subject: personInstance2)
def voteInstance2 = new Vote(voter: personInstance1, subject: personInstance3)
def voteInstance3 = new Vote(voter: personInstance1, subject: personInstance4)
def voteInstance4 = new Vote(voter: personInstance1, subject: personInstance5)
def voteInstance5 = new Vote(voter: personInstance2, subject: personInstance1)
voteInstance1.save()
voteInstance2.save()
voteInstance3.save()
voteInstance4.save()
voteInstance5.save()
this is my grails bootstrap-file , Antonio and Dennis have voted, and each need to be presented a list of people they didnt vote for.
EDIT:
this way i seem to get a result for Dennis, since he only voted once,
but if i put v.voter_id = 1,
to get a result for Antonio, the result doubles according to how many votes he did.
SELECT first_name FROM vote as v
LEFT OUTER JOIN person as p
ON v.subject_id != p.id AND v.voter_id = 2
WHERE p.id IS NOT NULL

Try this:
SELECT * FROM Person P
WHERE NOT EXISTS(
SELECT 'Vote' FROM Vote V
WHERE V.subject = P
)
In this way you'll extract all Person without Vote
EDIT
In SQL you can retrieve a matrix in this way:
CREATE TABLE #person (nome varchar(30))
CREATE TABLE #vote (votante varchar(30), candidato varchar(30))
INSERT INTO #person values
('Antonio Vivaldi'),
('Dennis Rodman'),
('Marc Oh'),
('Gudrun Graublume'),
('Hilde Feuerhorn'),
('Mandy Muller')
INSERT INTO #vote values
('Antonio Vivaldi', 'Dennis Rodman'),
('Antonio Vivaldi', 'Marc Oh'),
('Antonio Vivaldi', 'Gudrun Graublume'),
('Antonio Vivaldi', 'Hilde Feuerhorn'),
('Dennis Rodman', 'Antonio Vivaldi')
SELECT *
FROM #person p
CROSS JOIN #person c
WHERE NOT EXISTS(
SELECT 'X'
FROM #vote v
WHERE v.votante = p.nome
AND v.candidato = c.nome
)
AND p.nome <> c.nome
ORDER BY p.nome

Related

How to write fields many2one in Odoo with XMLRPC

I want to use the xmlrpc for the model "product.category".
How to write the field "categ_id" with "read and search"in PYTHON ? I have always an error. I need to do that because i have to transfer this informations into another database Odoo.
import xmlrpc.client
import time
url_db1="http://localhost:8069"
db_1='DB_ONE'
username_db_1='username'
password_db_1='password'
common_1 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url_db1))
models_1 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url_db1))
version_db1 = common_1.version()
print("details..", version_db1)
url_db2="http://localhost:806972"
db_2='DB_2'
username_db_2='myemail'
password_db_2='mypassword'
common_2 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/common'.format(url_db2))
models_2 = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url_db2))
version_db2 = common_2.version()
print("details..", version_db2)
uid_db1 = common_1.authenticate(db_1, username_db_1, password_db_1, {})
uid_db2 = common_2.authenticate(db_2, username_db_2, password_db_2, {})
db_1_categories = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.category', 'search_read', [[]], {'fields':['id','name', 'parent_id']})
total_count = 0
for category in db_1_categories:
print("category..", category)
total_count +=1
values = {}
values['id'] = category['id']
values['name'] = category['name']
if category['parent_id']:
values['parent_id'] = category['parent_id'][0]
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.category', 'create', [values])
print("Total Created..", total_count)
This is the error :
ValidationError: ('The operation cannot be completed: another model requires the record being deleted. If possible, archive it instead.\n\nModel: Product Category (product.category), Constraint: product_category_parent_id_fkey', None)\n'>
The method name should be search_read
Example:
models.execute_kw(db, uid, password,
'product.template', 'search_read',
[],
{'fields': ['name', 'default_code', 'categ_id']})
It should return a list of dicts, the categ_id field value is a list of two values, the first is the id of the category and the second is the name of the category.
To write categ_id, you just need to provide the category ID to the write method.
Example:
product_template_data = [{'default_code': 'FURN_6666', 'categ_id': [8, 'All / Saleable / Office Furniture'], 'id': 23, 'name': 'Acoustic Bloc Screens'}, ...]
for ptd in product_template_data:
models.execute_kw(db, uid, password, 'product.template', 'write',
[[ptd['id']],
{
'categ_id': ptd['categ_id'][0],
...
}
])
You mentioned that you need to transfer data to another database, the product template is probably not present which means that you can't call the write method instead, you can call the create method.
Example:
id = models.execute_kw(db, uid, password, 'product.template', 'create', [{
'categ_id': ptd['categ_id'][0],
...
}])
Edit:
You will get an invalid syntax error using:
[product,'categ_id': product['categ_id'][0],]
To pass values to the create method, you need to pass args to the execute_kw method as a list then pass values as a dictionary inside that list.
Edit:
values = {}
values['name'] = product['name']
values['categ_id'] = product['categ_id'][0]
...
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [values])
Edit: Use the parent category id in the new database
When we call the create method it will create a new record and return its ID which is probably different the one passed through the values dictionary.
To avoid the ValidationError you can use a dictionary where the parent ID in the old database is the key and the new ID is the value then you have just to pass that value when creating a new category.
category_ids = {}
for category in db_1_categories:
print("category..", category)
total_count +=1
values = {}
# values['id'] = category['id']
values['name'] = category['name']
if category['parent_id']:
values['parent_id'] = category_ids[category['parent_id'][0]]
category_id = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.category', 'create', [values])
category_ids[category['id']] = category_id
first of all error in your code
This is your code
db_1_products = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.template', 'search_read', [[]], {'fields':['id','name', 'categ_id','type', 'default_code', 'list_price', 'website_url', 'inventory_availibility', 'website_description']})
total_count = 0
for product in db_1_products:
print("produt..", product)
total_count +=1
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [product,'categ_id': product['categ_id'][0],])
print("Total Created..", total_count)
This is updated code
db_1_products = models_1.execute_kw(db_1, uid_db1, password_db_1, 'product.template', 'search_read', [[]], {'fields':['id','name', 'categ_id','type', 'default_code', 'list_price', 'website_url', 'inventory_availibility', 'website_description']})
total_count = 0
for product in db_1_products:
print("produt..", product)
total_count +=1
new_lead = models_2.execute_kw(db_2, uid_db2, password_db_2, 'product.template', 'create', [{product,'categ_id': product['categ_id'][0]}])
print("Total Created..", total_count)
you need to pass dictionary when creating of any model in odoo.

Add fields to model without permit param

am trying to add fields to a model directly from the controller action without a form,only the user_id is saved the other columns (firstname,lastname) are empty each time i run the code, below is the code, note: User has_many :provide_helps.
#firstname=current_user.firstname
#lastname=current_user.lastname
#gh_user = User.find_by status: 'gh'
#ph = #gh_user.provide_helps.create(firstname: "#{#firstname}" , lastname: "#
{#lastname}")
Try this
#ph = #gh_user.provide_helps.create(firstname: #firstname , lastname: #lastname)
No need to assign #first_name and #last_name using #{}.
you can use the code below:
#gh_user = User.find_by(status: 'gh')
#ph = #gh_user.provide_helps.new({ firstname: current_user.firstname, lastname: current_user.lastname })
#ph.save
or
#gh_user = User.find_by(status: 'gh')
#ph = #gh_user.provide_helps.new()
#ph.first_name = current_user.first_name
#ph.last_name = current_user.last_name
#ph.save

Rails find_by_name on another table

I have Genders and based on Gender name create category and subcategories.
m = Gender.create(:gender => 'masculine')
c = Category.find_by_name("T-shirt", gender: m )
c.subcategories.create(:name => "Necklace" )
and so on.
While the answer of Amit Sharma works I'd suggest have several improvements for it.
Use the new Hash syntax:
gender = Gender.create!(gender: 'masculine')
Use find_by instead of where/first
category = Category.find_by(gender: gender, name: 'T-Shirt')
Use the bang variants when not checking the return value
category.subcategories.create!(name: 'Necklace')
Use if/present? instead of unless/blank?
if category.present?
category.subcategories.create!(name: 'Necklace')
end
(this is just a matter of taste. But my brain seems to have troubles parsing unless expressions:-))
Use find_or_initialize_by/find_or_create_by!
If you want to find the category OR create it if it does not exist, use find_or_initialize/find_or_create_by!() so you can avoid the nil check:
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
So in total i'd write it like:
gender = Gender.create!(gender: 'masculine')
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
category.subcategories.create!(name: 'Necklace')
You can try this.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender ).first
c.subcategories.create(name: "Necklace" )
Please note above code will raise an exception if no category found with given condition, so to avoid that you can use following.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender).try(:first)
unless c.blank?
c.subcategories.create(name: "Necklace" )
end

Comparing Properties in hasMany Criteria

I have a domain classes like so:
class Person {
Team team
static hasMany = [history: PersonHistory]
}
class PersonHistory {
Team team
Person person
}
Now I would like to create a criteria that pulls back all the 'persons' who have a PersonHistory instance with a different team.
Something along the lines of:
def c = Person.createCriteria()
def experiment = c.list {
history {
neProperty("team", history.team)
}
}
However this is throwing (because of the history.team):
Missing Property Exception
No such property: history for class: grails.orm.HibernateCriteriaBuilder
Can I do this inside one criteria query? If so, how?
I haven't test it but I think this should work:
Person.withCriteria {
createAlias 'history', 'h'
ne 'team', h.team
}
You have to create an alias for the join with history.
EDIT:
Now I got it! With the following sample data:
def t1 = new Team(name: 'team 1').save()
def t2 = new Team(name: 'team 2').save()
def t3 = new Team(name: 'team 3').save()
def p1 = new Person(team: t1).save()
def p2 = new Person(team: t1).save()
def p3 = new Person(team: t2).save()
def p4 = new Person(team: t2).save()
def p5 = new Person(team: t3).save()
def ph1 = new PersonHistory(person: p1, team: t1).save()
def ph2 = new PersonHistory(person: p2, team: t3).save() // t3 instead of t1
def ph3 = new PersonHistory(person: p3, team: t2).save()
def ph4 = new PersonHistory(person: p4, team: t1).save() // t1 instead of t2
def ph5 = new PersonHistory(person: p5, team: t3).save(flush: true)
Now you can execute the following criteria:
List<Person> persons = PersonHistory.withCriteria {
createAlias 'person', 'p'
neProperty 'p.team', 'team'
projections {
property 'person'
}
}
That will return the correct persons p2 and p4

semicolon as statement separator in Rails Console

The Rails console doesn't seem to like multiple ruby statements on the same line separated by a semicolon. Whenever I do this, the next line starts with ?> and I find that only the first statement was executed. Do you have to put each statement on a separate line?
>> user = User.new
user = User.new
=> #<User id: nil, username: "", hashed_password: "", first_name: "", last_name: "", email: "", display_name: "", user_level: 0, created_at: nil, updated_at: nil, posts_count: 0>
>> user.username = "John"; hashed_password = "John"; first_name = "John"; last_name = "coltrane"; email = "John#coltrane.com"; display_name = "Johndispay"; user_level = 9;
user.username = "John"; hashed_password = "John"; first_name = "John"; last_name = "coltrane"; email = "John#coltrane.com"; display_name = "Johndispay"; user_level = 9;
?> user.save
user.save
=> true
Everything except user.username = "John"; was ignored
You need to say "user." so Ruby knows you mean to call the attribute assignment methods of the instance of user. Otherwise, you are just setting local variables called "hashed_password", etc.
>> user.username = "John"; user.hashed_password = "John"; user.first_name = "John"; user.last_name = "coltrane"; user.email = "John#coltrane.com"; user.display_name = "Johndispay"; user.user_level = 9;
Although, you could just pass a hash of the attributes you want to set on the new instance, like so
>> user = User.new(:username => "John", :hashed_password => "John", ...
It's the trailing ; on your input. When you put a ';' on the end IRB will assume you want to add another statement. If you leave it off, it will evaluate all the statements and return the return value of the last one.
Sometimes if the method I'm calling is going to return a large array I will do something like this...
a = Account.entries; a.size
This will save the values I need and just output the size of the array instead of trying to dump it to the console, which can take a long time if it's large.
are you sure you didn't mean
user.username = "John"; user.hashed_password = "John";
i tried
>> a = 1; b= 2
=> 2
>> a
=> 1
>> b
=> 2
when something doesn't work, you can use one rule: always reduce it to the simplest case.

Resources