Rails 4 - can't find entity in join table - sql

I have there model objects, Team, Post, User and a join table between Team and User called Member. Users in teams can vote on posts. When voting on a posts I want give the user who created the Post points. I therefore have a ´points integerattribute in myMember` model. I have a method where I give points to users which looks like this:
def give_points_to_user(post, increase)
member = post.user.members.where(team_id: post.team.id).first
if increase
member.points += 5
else
member.points -= 5
end
member.save!
end
Calling this method gives me this error:
undefined method `+' for nil:NilClass
So, how should my find (or where) call look if I want to find the correct member? That is, the member/user who created the post.

undefined method `+' for nil:NilClass
The problem is the points value is initially set to nothing,so member.points += 5 won't work because when you give member.points += 5 it is actually member.points = member.points+5.So you are actually giving member.points = nil+5,which fails giving that error.
Solution
As,#Mandeep said,you could make it work by setting a default value to points attribute in the table.Suppose if the value is zero(0) then the result of member.points += 5 would be 5.

Related

Why does Postgres not accept my count column?

I am building a Rails app with the following models:
# vote.rb
class Vote < ApplicationRecord
belongs_to :person
belongs_to :show
scope :fulfilled, -> { where(fulfilled: true) }
scope :unfulfilled, -> { where(fulfilled: false) }
end
# person.rb
class Person < ApplicationRecord
has_many :votes, dependent: :destroy
def self.order_by_votes(show = nil)
count = 'nullif(votes.fulfilled, true)'
count = "case when votes.show_id = #{show.id} AND NOT votes.fulfilled then 1 else null end" if show
people = left_joins(:votes).group(:id).uniq!(:group)
people = people.select("people.*, COUNT(#{count}) AS people.vote_count")
people.order('people.vote_count DESC')
end
end
The idea behind order_by_votes is to sort People by the number of unfulfilled votes, either counting all votes, or counting only votes associated with a given Show.
This seem to work fine when I test against SQLite. But when I switch to Postgres I get this error:
Error:
PeopleControllerIndexTest#test_should_get_previously_on_show:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column people.vote_count does not exist
LINE 1: ...s"."show_id" = $1 GROUP BY "people"."id" ORDER BY people.vot...
^
If I dump the SQL using #people.to_sql, this is what I get:
SELECT people.*, COUNT(nullif(votes.fulfilled, true)) AS people.vote_count FROM "people" LEFT OUTER JOIN "votes" ON "votes"."person_id" = "people"."id" GROUP BY "people"."id" ORDER BY people.vote_count DESC
Why is this failing on Postgres but working on SQLite? And what should I be doing instead to make it work on Postgres?
(PS: I named the field people.vote_count, with a dot, so I can access it in my view without having to do another SQL query to actually view the vote count for each person in the view (not sure if this works) but I get the same error even if I name the field simply vote_count.)
(PS2: I recently added the .uniq!(:group) because of some deprecation warning for Rails 6.2, but I couldn't find any documentation for it so I am not sure I am doing it right, still the error is there without that part.)
Are you sure you're not getting a syntax error from PostgreSQL somewhere? If you do something like this:
select count(*) as t.vote_count from t ... order by t.vote_count
I get a syntax error before PostgreSQL gets to complain about there being no t.vote_count column.
No matter, the solution is to not try to put your vote_count in the people table:
people = people.select("people.*, COUNT(#{count}) AS vote_count")
...
people.order(vote_count: :desc)
You don't need it there, you'll still be able to reference the vote_count just like any "normal" column in people. Anything in the select list will appear as an accessor in the resultant model instances whether they're columns or not, they won't show up in the #inspect output (since that's generated based on the table's columns) but you call the accessor methods nonetheless.
Historically there have been quite a few AR problems (and bugs) in getting the right count by just using count on a scope, and I am not sure they are actually all gone.
That depends on the scope (AR version, relations, group, sort, uniq, etc). A defaut count call that a gem has to generically use on a scope is not a one-fit-all solution. For that known reason Pagy allows you to pass the right count to its pagy method as explained in the Pagy documentation.
Your scope might become complex and the default pagy collection.count(:all) may not get the actual count. In that case you can get the right count with some custom statement, and pass it to pagy.
#pagy, #records = pagy(collection, count: your_count)
Notice: pagy will efficiently skip its internal count query and will just use the passed :count variable.
So... just get your own calculated count and pass it to pagy, and it will not even try to use the default.
EDIT: I forgot to mention: you may want to try the pagy arel extra that:
adds specialized pagination for collections from sql databases with GROUP BY clauses, by computing the total number of results with COUNT(*) OVER ().
Thanks to all the comments and answers I have finally found a solution which I think is the best way to solve this.
First of, the issue occurred when I called pagy which tried to count my scope by appending .count(:all). This is what caused the errors. The solution was to not create a "field" in select() and use it in .order().
So here is the proper code:
def self.order_by_votes(show = nil)
count = if show
"case when votes.show_id = #{show.id} AND NOT votes.fulfilled then 1 else null end"
else
'nullif(votes.fulfilled, true)'
end
left_joins(:votes).group(:id)
.uniq!(:group)
.select("people.*, COUNT(#{count}) as vote_count")
.order(Arel.sql("COUNT(#{count}) DESC"))
end
This sorts the number of people on the number of unfulfilled votes for them, with the ability to count only votes for a given show, and it works with pagy(), and pagy_arel() which in my case is a much better fit, so the results can be properly paginated.

error with rec.env['book.tickets'].search()

#api.depends('totalbook')
def _computebook(self):
sum_a = 0
for rec in self:
for l in rec.env['book.tickets'].search([('status', 'in', ('sold', 'rent'))]):
if l:
sum_a += 1
rec.currentbook = rec.totalbook - sum_a
I use this compute to calculate current book in library.
But when I run this code, the problem calculate of my each book base on all books.
When you are write any compute method in odoo, in self you will get all the list of browsable objects.
And then you are trying to search data from that browsable object. That's why you get a error.
You have to use self.env instead of rec.env.
Because you can't search data from browsable object you can access only data of that browsable object.
You have to add limit 1 on when you are searching a record of multiple record are than you will get another expected Singleton error.
Either you can you use another loop after searching record.
Let me know if you face any errors again.
Thanks
this error occured because
whenever you are trying to make a object of another model at that time you should have to use with self.env instead of rec.env because in your method rec is just a record-set of your instance
so please update your method as per the following snippet.
for l in self.env['book.tickets'].search([('status', 'in', ('sold','rent'))]):

Rails Order by frequency of a column in another table

I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.

rails 3, how can I run a loop n.times where the value of n is set by local variable

This should be fairly simply...
I have Recommendations has_many Assets.
I want to limit the user to adding 3 Assets per Recommendation, and I can do this simply by limiting the number of fields show.
In my new action in the controller I am doing a very simple:
3.times {#recommendation.assets.build}
In my edit action I am trying to build the logic to decide how many fields to show:
#assets = #recommendation.assets.all
if #assets.empty?
3.times {#recommendation.assets.build}
else
asset_loop = #assets.count - 3
asset_loop.times {#recommendation.assets.build}
end
The if works but the else does not. How can I make this work?
If I understand your goal, you just need to change
asset_loop = #assets.count - 3
to
asset_loop = 3 - #assets.count
Do make sure to validate the incoming data if you want to truly enforce the limit. Otherwise you're at the mercy of anyone with a web console and curl.

Rails 3 noob - getting value from application_controller to another controller

I understand that the application_controller will produce variable values for an entire application but have never applied that knowledge. There is a variable that comes up in most of my controllers... #qs for the number of questions. The following code is not working.
application_controller:
def qs
if current_user.sec_paid and current_user.min_paid
#qs = 36
elsif current_user.sec_paid
#qs = 25
else
#qs = 5
end
end
applets_controller:
#strengths = Strength.where("position <= ?", #qs).order("position")
applet1.html.erb:
<%=#qs.to_i%>
The output to a view file is just a way to test for the value of the variable and will be removed. Currently it produces a zero.
Any help is appreciated. Specific changes to my code is most appreciated.
Use before_filter :qs in your ApplicationControler (or in those controllers it is needed) to invoke the qs method on every call. In this way the #qs variable will be initialized.