Collection_select problem - ruby-on-rails

UPDATE: Solved. Thanks BusyMark!
EDIT: This is revised based on the answer below from BushyMark. I think I made all the suggested changes correctly, but I still can't get the post_type_id to save to the database when a user submits a message.
My app has a profile page. On the profile page, the owner of the page can type an update and hit "submit." The update messages appear without the page reloading (i.e. Ajax).
It basically looks like your Twitter home page. BUT, there's also a message "type" that our users select when typing an update. The message type is a pre-populated list. I've made it a model and load it as seed data in the app.
So: there is a profile model, a post model, and a post_type model.
profile has_many :posts
post belongs_to :post_type
post_type has_many :posts
The post model also has this: attr_accessible :message, :post_type_id
Routes.rb looks like this:
map.resources :users, :has_one => :profile, :has_many => :posts
map.resources :post_types
The posts controller has this method for creating the posts:
def create
#post = current_user.profile.posts.build(:message => params[:message], :post_type_id => params[:post_type_id])
if #post.save
redirect_to user_profile_path
else
flash[:notice] = "Message failed to save."
redirect_to user_profile_path
end
end
The "show" view for the profile page where this all occurs looks like this:
<% form_tag(:controller => "posts", :action => "create") do %>
<%= label_tag(:message, "Update your people") %><br />
<%= text_area_tag(:message, nil, :size => "27x3") %>
<div class="updateSubmit"><%= submit_tag("Update") %></div>
<%= label_tag(:type_id, "Optional: Update type", :class => 'typeLabel') %>
<%= collection_select(:post, :post_type_id, PostType.all, :id, :name, {:prompt => true}) %>
<% end %>
<% #profile.profile.posts.each do |c| %>
<%=h c.message %></div>
<p><%=h c.post_type %></p>
<p>Posted <%=h time_ago_in_words(c.created_at) %> ago</p>
<% end %>
That's the background. Here's the problem. With my current setup, when the user submits his message, the message gets sent to the posts database table, BUT the post_id does not.
My select tag is rendering like this:
<select id="post_post_type_id" name="post[post_type_id]"><option value="">Please select</option>
This is the output I get in the log when I submit a post:
Processing PostsController#create (for IP at 2009-09-03 03:03:08) [POST]
Parameters: {"message"=>"This is another test.", "commit"=>"Update", "action"=>"create", "authenticity_token"=>"redacted", "post"=>{"post_type_id"=>"3"}, "controller"=>"posts", "user_id"=>"1"}
User Load (0.3ms) SELECT * FROM "users" WHERE ("users"."id" = 1)
Profile Load (0.7ms) SELECT * FROM "profiles" WHERE ("profiles".user_id = 1) LIMIT 1
Post Create (0.5ms) INSERT INTO "posts" ("message", "updated_at", "post_type_id", "profile_id", "created_at") VALUES('Hello', '2009-09-03 20:40:24', NULL, 1, '2009-09-03 20:40:24')
Here's the relevant parts of my schema, updated per BushyMark's comments:
`create_table "post_types", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end`
`create_table "posts", :force => true do |t|
t.text "message"
t.integer "profile_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "post_type_id"
end`

Couple things going on here I see so far:
type is a reserved word in Rails. It is used for single table inheritance. If you change your column name to something like "post_type" it should help immensely in trying to solve random issues. I went through this a while back and was beating my brains out before I knew about STI.
Also, you have a type_id in your post . . . but I didn't see that your post :belongs_to :type . . . don't forget that a model with the foreign key needs this declaration to use all the ActiveRecord associations.
Hope this helps!
Another Edit: I noticed that in your insert statement, it appears you have a "type" column? This will also cause issues when using relationships. Are you using a separate 'type' model? Can you possibly post your schema if any of the above statements don't help?
In ActiveRecord, a type column is reserved and will not display properly(back to my statement about Single Table Inheritance). At this point I strongly recommend getting rid of your type and type_id columns, and giving your Post a "post_type_id" column.
Then once you create a separate post_type model that has_many :posts you will want to make sure your Post :belongs_to :post_type (because it has the foreign key). Once you do that, you will be able to call post_object.post_type and it will return the model relationship you are looking for.

Related

Render of object missing the specified nested include

I am migrating a Rails app from 3.2 to 5.1, and it includes a simple enough model which includes a reference to the product type of the batch,
create_table "batches", :force => true do |t|
t.string "code"
t.integer "product_id"
t.boolean "recalled", :default => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
where, (edited down for clarity)
class Batch < ApplicationRecord
validates_presence_of :product
belongs_to :product
end
I am using will_paginate, so I have the following extract in my model to return a table of records,
def self.search(search, page)
if search
Batch.not_recalled.
where('batches.code LIKE ? OR products.code LIKE ? OR products.name LIKE ?',
"%#{search}%", "%#{search}%", "%#{search}%").
references(:product).
includes(:product).
order(:code).
page(page).per_page(30)
else
Batch.not_recalled.
includes(:product).
order(:code).
page(page).per_page(30)
end
end
And correspondingly, in my controller, I have,
# GET /batches/paginated
def paginated
#batches = Batch.search(params[:search], params[:page])
respond_to do |format|
format.html # paginated.html.erb
format.json { render json: #batches.to_json(include: [:product]) } # note the explicit 'include'
end
end
And in the partial to render the object, I have
<%= product = batch[:product] %> # Try and extract the nested record
<%= puts 'debug' %>
<%= puts batch.inspect %>
<%= puts product.inspect %>
<%= puts 'end debug' %>
<tr role="row" %> >
<td ><%= batch[:code] %></td>
<td><%= product ? product[:duplicate_batch] : 'N/A' %></td>
<td><%= product ? product[:code] : 'N/A' %></td>
<td><%= product ? product[:name] : 'N/A' %></td>
<td><%= link_to 'Show', batch %></td>
</tr>
Now, product is always nil, and the inspect shows the product nested record is NOT included.
I can also examine the JSON, and I can see the included 'product' entry ONLY if I explicitly include it in the format.json line of the controller, as shown above. As there isn't an equivalent syntax to specify the include during the format for the HTML render, I'm not able to render the nested product in HTML.
The really curious thing is I have another very similar model in the same app where this works just fine for HTML too, without an explicit include.
I am aware that ActiveRecord does not load until the last minute, but I am using includes with will_paginate, and I don't see why that would be an issue here. The inspect shows the record, not a ActiveRecord query (I believe).
Two questions:
Why is the nested include not included automatically?
If there's a
good reason for that, how do I specify it to include the product in
the HTML render?
Thanks.
add into model
scope :search, -> (search_param, page) do
includes(:product).
order(:code).
paginate_page(page)
end
Add there also search find part and u should be good
Also i would use activeModelSerializer for serializing objects or
https://github.com/Netflix/fast_jsonapi
Also go rails console
Batch.search(code, 1).to_json(include: [:product]) see whats gets displayed

Rails select from database

Can somebody explain this ? , it is from ruby guides
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
I have an attachment model, I want to select its version with combobox when I create an object, and also I want a new version option.
here what attachment has
def change
create_table :attachments do |t|
t.string :filename
t.attachment :file
t.string :version
t.text :description
t.timestamps null: false
end
UPDATE:
<%= f.collection_select( :version, Attachment.where.not(version: nil), :version, :version) %>
it is working like that, but I don't understand,
Try this to avoid the nil value of the version:
collection_select(:f, :attachment_id, Attachment.where.not(version: nil), :id, :version)
Explanation of How collection_select Works:
collection_select(
:f, # field namespace
:attachment_id, # field name
# result of these two params will be: <select name="f[attachment_id]">...
# then you should specify some collection or array of rows.
# In your example it is:
Attachment.where.not(version: nil)
# Then, you should specify methods for generating options
:id, # this is name of method that will be called for every row, result will be set as key
:version # this is name of method that will be called for every row, result will be set as value
)
See this and this for more information.
check accepted answer from this thread for explanation on how collection_select works: Can someone explain collection_select to me in clear, simple terms?
In this select:
<%= collection_select(:f, :attachment_id, Attachment.all, :id, :version) %>
you display all versions from already created attachments, so if attachments table is empty you will get null

how to associate checkbox used in view to update a field in a RoR table?

I have created a new field called "Reviewed" in one of my tables called "Posts".
Everytime someone checkmarks prior to their post, I want it to be Reviewed for approval prior to display.
The "Reviewed" field in the table is defaulted to True.
Here's the schema.db
create_table "posts", :force => true do |t|
t.text "content"
t.integer "user_id"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.boolean "reviewed", :default => true
Here's the code in the view that I'm using for the checkmark
<%= label_tag(:post_reviewed, "Review this post") %>
<%= check_box_tag(:post_reviewed) %>
I want it so that every time this checkbox is checked, the reviewed field is set to False so that the post is not displayed until I review it and update it to True. How can I do this?
You probably have something like this in your view:
<%= form_for :post do |f| %>
..
<% end %>
So, just try to add to it:
<%= f.check_box :reviewed %>
That should automatically associate it to the desired object.
Let me know if that works for you.
Sounds like your doing everything backwards. If you want to go down that route, than you should change it to review and only display items scoped to where(review: false). That way, if a user wants a post to be reviewed prior to publishing, then they would check the checkbox.
I don't know what the process is, but if all posts MUST be reviewed, why not change the field to reviewed_at and make it a datetime. Then you only show posts where('reviewed_at IS NOT NULL'). This is similar to deleted_at or published_at.
I hope this helps.

Ruby on Rails ActiveRecord::AssociationTypeMismatch

I'm trying to create a private messaging system for my website, and I am currently working on message replies. I've run into an ActiveRecord::AssociationTypeMismatch problem however. This is the error message:
ActiveRecord::AssociationTypeMismatch in RepliesController#create
Message(#58297820) expected, got String(#1635350)
I've been trying to figure out what the problem is for awhile now with no luck.
Below you will find my code for my migration, model, view, and controller.
Migration
def self.up
create_table :replies do |t|
t.integer :message_id, :null => false
t.integer :sender_id, :null => false
t.text :message, :null => false
t.timestamps
end
end
Model
class Reply < ActiveRecord::Base
belongs_to :message
validates_presence_of :message
cattr_reader :per_page
##per_page = 10
end
View
<% form_for(#reply) do |f| %>
<table>
<tr>
<td><%= f.text_area :message %></td>
</tr>
<%= f.hidden_field :message_id, :value => #message.id %>
<tr>
<td><%= f.submit 'Reply', :id => 'replySubmit' %></td>
</tr>
</table>
<% end %>
Controller
def create
account = Account.getAccountById(session[:user])
message = Message.find(
params[:reply][:message_id],
:conditions => ["messages.account_id=? or messages.sender_id=?", account.id, account.id]
)
if message
#reply = Reply.new
#reply.message_id = message.id
#reply.sender_id = account.id
#reply.message = params[:reply][:message]
if #reply.save
flash[:message] = "Reply successfully submitted."
redirect_to(messages_path)
else
flash[:warning] = "Message cannot be blank."
redirect_to(messages_path)
end
else
redirect_to(messages_path)
end
rescue ActiveRecord::RecordNotFound
render :template => "error"
end
I'd appreciate any help provided. I'll keep trying to figure out what the problem is.
Thank you.
Update: Stacktrace
RAILS_ROOT: C:/Users/redbush/Desktop/biomixr
Application Trace | Framework Trace | Full Trace
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record /associations/association_proxy.rb:259:in `raise_on_type_mismatch'
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations/belongs_to_association.rb:22:in `replace'
C:/Ruby/lib/ruby/gems/1.8/gems/activerecord-2.3.8/lib/active_record/associations.rb:1287:in `message='
C:/Users/redbush/Desktop/biomixr/app/controllers/replies_controller.rb:14:in `create'
Request
Parameters:
{"commit"=>"Reply",
"reply"=>{"message"=>"sssss",
"message_id"=>"4"},
"authenticity_token"=>"SMVfiolNAVPmLLU0eOWzx2jPFbujMtpyqQcs6A2Mxr0="}
Show session dump
Response
Headers:
{"Content-Type"=>"",
"Cache-Control"=>"no-cache"}
Your problem is that you have a relation to message and you also want to use a text field on the same model called message. When you create a relation you have access to some new methods that will step on the feet of other getter and setters.
To fix this change the name of the text field to another name and reflect the changes in your controller.
If you need more detail let me know.
The belongs_to relation creates a message attribute on your model, which expects an object of type Message. However, you also have a message attribute of type string. You can workaround this by using:
belongs_to :my_message, :class_name => :message
And the related message will be available as my_message, while the text field will be available as message.
In general, it seems like you're trying to handle too much of the relation work on your own - let rails do the work for you.
You have a clash in the design of your model. You have both an association and an attribute named message.
The error you are getting is because Rails is expecting you to fill up the association instead of the attribute.
I suggest changing your attribute from message to message_text, like so:
#reply = Reply.new
#reply.message_id = message.id
#reply.sender_id = account.id
#reply.message_text = params[:reply][:message]
Do make the changes in your migration file and run it too.

Modifying attributes on the join model with accepts_nested_attributes_for

Simply, a Contact can have various associated Time Windows, which may or may not be Active as a Schedule. To wit:
Models
class Contact < ActiveRecord::Base
has_many :schedules
has_many :time_windows, :through => :schedules
accepts_nested_attributes_for :schedules, :allow_destroy => true
end
class TimeWindow < ActiveRecord::Base
has_many :schedules
has_many :contacts, :through => :schedules
end
class Schedule < ActiveRecord::Base
belongs_to :contact
belongs_to :time_window
end
View
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<p>
<%= f.label tw.description %>
<%= hidden_field_tag "contact[schedules_attributes][][id]", schedule.id %>
<%= check_box_tag "contact[schedules_attributes][][time_window_id]",
tw.id, #contact.time_windows.include?(tw) %>
<%= check_box_tag "contact[schedules_attributes][][active]", nil,
schedule.active %>
</p>
<% end %>
This submits something like this:
Parameters: { "commit" => "Update", "contact" => {
"group_ids" => ["2"], "enabled" => "1",
"schedules_attributes" => [ { "time_window_id"=>"1", "id"=>"46"},
{ "time_window_id" => "2", "id" => "42", "active" => "on" },
{ "time_window_id" => "3", "id" => "43"},
{ "time_window_id" => "4", "id" => "44", "active" => "on"}],
"last_name" => ...
The update action in the controller is basically stock, except to handle another instance of another related model which I coded using the "Handling Multiple Models" example from the Advanced Rails Recipes book.
According to this API doc, I think the above ought to work. However, nothing about the Schedules is getting updated. This shows up in the server log:
[4;35;1mSchedule Update (0.2ms)[0m [0mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 42[0m
[4;36;1mSchedule Update (0.1ms)[0m [0;1mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 44[0m
(NetBeans is giving me those stupid "[0m"'s in the output. I don't know what's wrong there.)
The SQL shows that the "active" boolean field is getting set to 0 where checked. How do I get this to correctly set the active bit?
As a followup, how would I organize this to get rid of the Schedule "connection" at all? I'm thinking I need to submit a :_delete with the Schedule from the form, but how would I do that conditionally when a checkbox is involved?
Thanks for any help you can provide. Rails is turning out to be a vast subject for me, and I want to do it "right." I'm really close here, but there's got to be a way to make this -- not just correct -- but elegant. The view code just feels way too cumbersome to be proper Rails. ;-)
I've kept trying different approaches to this problem, and I've come up with this, which works. Mostly. The only problem is that it doesn't handle NOT having a "Schedule" for each "Time Window". The form will render, and I'll get a disabled check_box (to prevent me from trying to delete something that isn't there), but I don't have a way to add it back, and submitting without it throws off the params hash (and causes Rails to give me an "Expected Hash (got Array)" error)
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<% f.fields_for "schedules_attributes[]", schedule do |sf| %>
<p>
<%= sf.label tw.description %>
<%= sf.hidden_field :id %>
<%= sf.check_box :_destroy, :disabled => schedule.new_record? %>
<%= sf.check_box :active %>
</p>
<% end %>
<% end %>
Note that the "schedules_attributes[]" array will automatically give you an existing ID within the braces in your HTML (which is nice), but the _attributes hash is expecting an "id" alongside the other attributes in order to make sense of the sub-hashes.
One of the big lessons I've learned here is that the "check_box_tag" method doesn't (seem to) give me a paired-up hidden field for Rails to parse in the unchecked case. I would have expected this. Adding one in by hand made a mess, which led me to finally giving into the "fields_for" method, and trying many incarnations before finding the appropriate syntax to get what I wanted out of it.
I've realized that my model isn't quite appropriate in this setup, so I'm going to change it, but I was so close to this answer, I wanted to at least get to the point of being able to see the end before I moved on.

Resources