Rails where, sort and combine same table - ruby-on-rails

I have a model called extra. Here how it looks like;
#<Extra id: nil, name: nil, price: nil, per: nil, compulsory: nil, online_payment: nil, payment_per_person: nil, is_included: nil, created_at: nil, updated_at: nil, user_id: nil>
I would like to first sort them by is_included where it is true. Then I want to sort where compulsory is true, lastly where compulsory is false. Then I want to combine them.
Basically, I want to group them because user can save them unordered. Then remove if anyone exists twice.
Shall I do it on the view or before_save callback?
Thanks

Extra.order('is_included desc, compulsory desc') will return your desired result and you should never execute SQL queries on view.

Related

Rails Active Record: Missing column in group query

I saw that someone had the same question but the solution was not clear to me:
Rails ActiveRecord: Missing column in grouping query
I wrote a query in Active Record as follows:
shortened_urls.joins(:visits)
.group(:short_url, :long_url)
.select('long_url, short_url, COUNT(visits.id) as number_of_visits')
.order(Arel.sql('COUNT(visits.id) DESC'))
.limit(5)
And I get the following results:
[#<ShortenedUrl:0x00005585fbb8fb60
id: nil,
long_url: "jfjfhgfhduiuyyyyyyyyyyyyyyyyyyyyyyyyyy.k",
short_url: "Bn_N2QPoSE1VlNYqWXUL9A">,
#<ShortenedUrl:0x00005585fbb8f9f8
id: nil,
long_url:
"https://help.ubuntu.com/community/PostgreSQL#restarting_the_server",
short_url: "Dte6_CUnAdxJgv_jUBwXIg">,
#<ShortenedUrl:0x00005585fbb8f8b8
id: nil,
long_url: "jjjfhusirtyuizyfsvqdhjdhdfv",
short_url: "CQa8n1o-f2oEwKy8j_Zo9Q">,
#<ShortenedUrl:0x00005585fbb8f778
id: nil,
long_url: "uezfhueizyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.kkkk",
short_url: "4S1uC8yl0a8HlK6xJq23FQ">,
#<ShortenedUrl:0x00005585fbb8f638
id: nil,
long_url: "blahffhhfhfhhfhfhffhfhhfhhfhfhfhfhhfhfh.com",
short_url: "_NZSroaeyvVqvOKoPAieng">]
As you can see, the column 'number_of_visits' doesn't appear and the id's are all nil;
Does anyone have any idea how to solve this ? Or maybe explain to me what was the solution for the other similar question posted?
Thank you very much

How to display and object relation when some records might not have its value associated yet?

I'm starting to learn Rails and I don't know what's the best practices from the language yet.
I have scaffolded Ticket and Status models (a ticket has its current status). In my database I have records that has no Status associated to (ticket.status is nil), but I also have records that has a Status already associated (ticket.status has #<Status> object).
When I'm listing all tickets in a table (/tickets url) I'm listing the tickets and displaying the status name (status.nome) like this:
%td= ticket.status.nome
But I get an error when some records has no status associated: undefined method 'nome' for nil:NilClass
How should I proceed in this case?
--
updated
Ticket.all gives me:
#<Ticket id: 5, titulo: "aeeee", descricao: "desc", user_id: nil, status_id: nil, created_at: "2015-09-11 07:01:38", updated_at: "2015-09-11 07:01:38">
#<Ticket id: 6, titulo: "teste", descricao: nil, user_id: nil, status_id: 1, created_at: "2015-09-11 07:09:45", updated_at: "2015-09-11 07:09:45">
I use delegate and it works like a charm.
In your Ticket model do:
delegate :nome, to: :status, prefix: true, allow_nil: true
and you can call it like
ticket.status_nome
undefined method 'nome' for nil:NilClass
For a quick solution, you can use try
%td= ticket.status.try(:nome)
which will silence the error.

Rails/ActiveRecord has_many through: association on unsaved objects

Let's work with these classes:
class User < ActiveRecord::Base
has_many :project_participations
has_many :projects, through: :project_participations, inverse_of: :users
end
class ProjectParticipation < ActiveRecord::Base
belongs_to :user
belongs_to :project
enum role: { member: 0, manager: 1 }
end
class Project < ActiveRecord::Base
has_many :project_participations
has_many :users, through: :project_participations, inverse_of: :projects
end
A user can participate in many projects with a role as a member or a manager. The connecting model is called ProjectParticipation.
I now have a problem using the associations on unsaved objects. The following commands work like I think they should work:
# first example
u = User.new
p = Project.new
u.projects << p
u.projects
=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil>]>
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
So far so good - AR created the ProjectParticipation by itself and I can access the projects of a user with u.projects.
But it does not work if I create the ProjectParticipation by myself:
# second example
u = User.new
pp = ProjectParticipation.new
p = Project.new
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
u.projects
=> #<ActiveRecord::Associations::CollectionProxy []>
Why are the projects empty? I cannot access the projects by u.projects like before.
But if I go through the participations directly, the project shows up:
u.project_participations.map(&:project)
=> [#<Project id: nil>]
Shouldn't it work like the first example directly: u.projects returning me all projects not depending on whether I create the join object by myself or not? Or how can I make AR aware of this?
Short answer: No, second example won't work the way it worked in first example. You must use first example's way of creating intermediate associations directly with user and project objects.
Long answer:
Before we start, we should know how has_many :through is being handled in ActiveRecord::Base. So, let's start with has_many(name, scope = nil, options = {}, &extension) method which calls its association builder here, at the end of method the returned reflection and then add reflection to a hash as a cache with key-value pair here.
Now question is, how do these associations gets activated?!?!
It's because of association(name) method. Which calls association_class method, which actually calls and return this constant: Associations::HasManyThroughAssociation, that makes this line to autoload active_record/associations/has_many_through_association.rb and instantiate its instance here. This is where owner and reflection are saved when the association is being created and in the next reset method is being called which gets invoked in the subclass ActiveRecord::Associations::CollectionAssociation here.
Why this reset call was important? Because, it sets #target as an array. This #target is the array where all associated objects are stored when you make a query and then used as cache when you reuse it in your code instead of making a new query. That's why calling user.projects(where user and projects persists in db, i.e. calling: user = User.find(1) and then user.projects) will make a db query and calling it again won't.
So, when you make a reader call on an association, e.g.: user.projects, it invokes the collectionProxy, before populating the #target from load_target.
This is barely scratching the surface. But, you get the idea how associations are being build using builders(which creates different reflection based on the condition) and creates proxies for reading data in the target variable.
tl;dr
The difference between your first and second examples is the way their association builders are being invoked for creating associations' reflection(based on macro), proxy and target instance variables.
First example:
u = User.new
p = Project.new
u.projects << p
u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
#=> #target = [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]
u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]>
#=> #target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]
u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> #target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>
Second example:
u = User.new
pp = ProjectParticipation.new
p = Project.new
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> #proxy = nil
#=> #target = []
u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>
#=> #target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]
u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> #target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>
There's no proxy for BelongsToAssociation, it has just target and owner.
However, if you are really inclined to make your second example work, you will just have to do this:
u.association(:projects).instance_variable_set('#target', [p])
And now:
u.projects
#=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
In my opinion which is a very bad way of creating/saving associations. So, stick with the first example itself.
This is more of a rails structure thing at the level of the ruby data structures.
To simplify it lets put it this way.
First of all imagine User as a data structure contains:
project_participations Array
projects Array
And Project
users Array
project_participations Array
Now when you mark a relation to be :through another (user.projects through user.project_participations)
Rails implies that when you add a record to that first relation (user.projects) it will have to create another one in the second realation (user.project_participations) that is all the effect of the 'through' hook
So in this case,
user.projects << project
#will proc the 'through'
#user.project_participations << new_entry
Keep in mind that the project.users is still not updated because its a completely different data structure and you have no reference to it.
So lets take a look what will happen with the second example
u.project_participations << pp
#this has nothing hooked to it so it operates like a normal array
So In conclusion, this acts like a one way binding on a ruby data structure level and whenever you save and refresh your objects, this will behave the way you wanted.
At the risk of some serious oversimplification let me try to explain what is going on
What Most of the other answers are trying to tell you is that these objects have not been linked yet by active record until they are persisted in the DB. Consequently the association behavior that you are expecting is not fully wired up.
Notice that this line from your first example
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
Is identical to the result from your second example
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
This statement from your analysis of what you think rails is doing is inaccurate:
So far so good - AR created the ProjectParticipation by itself and I
can access the projects of a user with u.projects.
AR record has not created the ProjectParticipation. You have declared this relationship in your model. AR is just returning proxy for the collection that it will have at some point in the future, which when populated assigned, etc, you will be be able to iterate over and query its members etc.
The reason that this works:
u.projects << p
u.projects
=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil>]>
But this doesn't
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
u.projects
=> #<ActiveRecord::Associations::CollectionProxy []>
Is that in the first case you are just adding objects to an array that your user instance has direct access to. In the second example the has_many_through relationship reflects a relationship that happens at the database level. In the second example in order for the your projects to be accessible through your user, AR has to actually run a query that joins the tables and returns the data you are looking for. Since none of these objects is persisted yet that database query can't happen yet so all you get back are the proxies.
The last bit of code is misleading because it is not actually doing what you think.
u.project_participations.map(&:project)
=> [#<Project id: nil>]
In this case you have a user which is directly holding an array of ProjectParticipations one of which is directly holding a project so it works. It is not actually using the has_many_through mechanism in they way you think.
Again this is a bit of an oversimplification but that is the general idea.
Associations are defined on database level and make use of database table's primary key (and in polymorphic cases, class name). In case of has_many :through the lookup on association (say, User's Projects) is:
Fetch all User-Project pairs, whose user_id is a certain value (primary key of an existing User in the database)
Fetch all project_id (primary keys of projects) from these pairs
Fetch all Projects by resulting keys
Of course, these are simple terms, in database terms it's much shorter and uses more complicated abstractions, such as an inner join, but the essence is the same.
When you create a new object via new, it is not yet saved in the database, and therefore has no primary key (it's nil). That said, if the object is not in a database yet, you have no way of referencing it from any ActiveRecord's association.
Side note:
There is a possibility, however, that a newly created (and not saved yet) object will act as if something is associated with it: it might show entries belonging to NULL. This usually means you have an error in your database schema that allows such things to happen, but hypothetically, one could design his database to make use of this.

In Rails 4 how do I write code that will retrieve objects instead of active record relations?

I have this code:
Sectionheader.where(:doc_id => #doc_id)
which returns:
#<ActiveRecord::Relation
[#<Sectionheader id: 1, section_id: nil, content: "a15+,f15+,a15+,f15+,a15+,f15+,a15+,f15+", created_at: "2014-08-13 18:18:39", updated_at: "2014-08-13 18:18:39", documentname: nil, doc_id: 1, row_number: 3, mergedsectionheader_id: nil>,
#<Sectionheader id: 2, section_id: nil, content: "A50+,F50+,A50+,F50+,A50+,F50+,A50+,F50+", created_at: "2014-08-13 18:18:39", updated_at: "2014-08-13 18:18:39", documentname: nil, doc_id: 1, row_number: 12, mergedsectionheader_id: nil>,
This result set is an array of activerecord relation objects. How can I instead get objects of type Sectionheader?
There used to be this method but it seems its been deprecated in rails 4
example this will return an array of person objects.
Person.find(1, :conditions => "administrator = 1", :order => "created_on DESC")
http://apidock.com/rails/ActiveRecord/Base/find/class
As Sergio Tulentsev commented above.
If you want to get an array of Sectionheader records from a relation, you can call #to_a
Sectionheader.where(:doc_id => #doc_id).to_a
If you wanted to eager-load the relation, you can call #load
Sectionheader.where(:doc_id => #doc_id).load
If you wanted to skip AR model instantiation completely, you could call #pluck(col1, col2, ...). This will return a multi-dimentional array representing the records.
Sectionheader.where(:doc_id => #doc_id).pluck(:id, :section_id, :content)
What you actually have is a relation object representing your result set. If you were to perform an operation on it which required actual data from the database, it would resolve into an array style object. You can see this in action in the response, where the console is showing you actual data.
#<ActiveRecord::Relation
[#<Sectionheader id: 1, section_id: nil, content: "a15+,f15+,a15+,f15+,a15+,f15+,a15+,f15+", created_at: "2014-08-13 18:18:39", updated_at: "2014-08-13 18:18:39", documentname: nil, doc_id: 1, row_number: 3, mergedsectionheader_id: nil>,
#<Sectionheader id: 2, section_id: nil, content: "A50+,F50+,A50+,F50+,A50+,F50+,A50+,F50+", created_at: "2014-08-13 18:18:39", updated_at: "2014-08-13 18:18:39", documentname: nil, doc_id: 1, row_number: 12, mergedsectionheader_id: nil>, ...
What this actually means is that #<ActiveRecord::Relation is the container. [ shows that an array type object has been opened. #<Sectionheader id: 1, ... is the first element of the array, and so on.
If you hadn't ended your statement there (dumping it to the console and forcing ActiveRecord to make the query) you would have a relation object representing the SQL of your query. You could add to this (with a select, or conditions or ordering etc.) if you wished without incurring the penalty of further database look-ups.
For most situations you can (and probably should) just treat this as an array. You can do a .to_a on the relation if your normal array methods aren't working, or .attributes on a single object to get a hash of the values returned by the query.
For example, you could do:
Sectionheader.where(:doc_id => #doc_id).each do |sectionheader|
puts sectionheader.doc_id
end
quite happily.

has value in a console but it's nil in my controller and my view?

I have this strange behavior and I don't undertand why. Here is the thing:
I have this record in my payment model:
1.9.3p286 :019 > u.payment.last
Payment Load (0.3ms) SELECT "payments".* FROM "payments" WHERE "payments"."user_id" = 10
=> [#<Payment id: 37, bank_name: "Mercantil", plan: "Plan Uno", date: "2012-12-25", reference_number: "3452435", coupon: "", user_id: 10, created_at: "2012-12-25 21:56:12", updated_at: "2012-12-25 21:58:31", active_until: "2013-01-24">]
As you can see, I have one record for my user: 10.
If I try to get the same information in my controller I don't get any exception, but
#user.payment.last.active_until
is empty, the same in the view.
For example if I try this in my view:
<%= #user.payment.last.active_until %>
I didn't get anything, is blank.
If I try this
<%= #user.payment %>
I got the same as my console
[#<Payment id: 37, bank_name: "Mercantil", plan: "Plan Uno", date: "2012-12-25", reference_number: "3452435", coupon: "", user_id: 10, created_at: "2012-12-25 21:56:12", updated_at: "2012-12-25 21:58:31", active_until: "2013-01-24">, #<Payment id: nil, bank_name: nil, plan: nil, date: nil, reference_number: nil, coupon: nil, user_id: 10, created_at: nil, updated_at: nil, active_until: nil>]
I really don't understand what happend here. Any help please.
Thanks in advance.
PD: The user could has many payments, but I need just the last.
It's not the same as in your console. In the console you get have one record, but in the view you get two - the record that you see in the console, and another blank one.
I'm guessing the blank one is the one that you are using for the form tag helper for the form used to create a new Payment record, but that's also the one that last returns. Either take the one before last, or filter for saved records before you call last.

Resources