Rails many-to-many creation fails - ruby-on-rails

I have event and band models, which have a many-to-many relationship through event_bands. I am trying to change my create method to use chosen jQuery like in episode 258 of railscasts. I am not sure how to read the message from my localhost when I try to create an event:
Started POST "/events" for 127.0.0.1 at 2014-03-16 17:11:07 +0900
Processing by EventsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Jx1Cm09uCwJcnY8573ZTRKMjH1BHWhlREFCfhij/AB0=", "event"=>{"name"=>"pojpj", "ko_name"=>"", "band_ids"=>["", "110"], "venue_id"=>"", "date(1i)"=>"2014", "date(2i)"=>"3", "date(3i)"=>"16", "time"=>"", "contact"=>"", "facebook"=>"", "ticket"=>"true", "price"=>"", "door_price"=>"", "ticket_url"=>"", "info"=>"", "info_ko"=>""}, "commit"=>"등록", "locale"=>"ko"}
Band Load (0.2ms) SELECT "bands".* FROM "bands" WHERE "bands"."id" = ? LIMIT 1 [["id", 110]]
(0.1ms) begin transaction
Band Exists (1.0ms) SELECT 1 AS one FROM "bands" WHERE ("bands"."name" = '...Whatever That Means' AND "bands"."id" != 110) LIMIT 1
(0.2ms) rollback transaction
Redirected to http://localhost:3000/events/new
It looks like it fails because the Band already exists in the database, but why is it doing that instead of creating the relation?
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
if #event.save
flash[:notice] = "Event Created!"
redirect_to event_path(#event)
else
flash[:notice] = "Event not created!"
redirect_to new_event_path
end
end
private
def event_params
params.require(:event).permit(:name,
:ko_name,
:avatar,
:time,
:facebook,
:ticket,
:door_price,
:ticket_url,
:info_kr,
:contact,
:price,
:info,
:info_ko,
:venue_id,
:date,
band_ids: [])
end
Relevant part of the form:
<p>
<%= f.label "Bands" %><br />
<%= f.collection_select :band_ids, Band.order(:name), :id, :name, {}, {multiple: true} %>
</p>
I have accepts_nested_attributes_for :bands in the model and I think the relation is setup correctly because I can do a = Event.new(name: 'asdf', band_ids: [1,5]) a.save and it is persisted.
update: it seems the problem is coming from the empty item in band_ids. For some reason, rails is setting the param as band_ids: ['',3,5]. I can replicate the SQL message from my server by trying to create a new entry in the console like this: a = Event.create(name: 'asdfasdfasdfasdf2345', band_ids: ['', 3, 2]). But where is the empty first item coming from?
edit2: Disregard the above, it seems the problem is actually that there is no event_id to use in the association?
#messages={:"event_bands.event_id"=>["에 내용을 입력해 주세요"]}

Related

rails form with validation always failing

I'm stuck on this... Asked a few questions already, but can't get my head around this.
I have a form for adding bibliography (model Biblio) that has a simple validation field on title of the bibliography.
Validation always fails, even when valid data is added.
MODEL
class Biblio < ApplicationRecord
# validates_presence_of :auteurs => there's a nested form too but
# I commented it out in order to isolate the problem
accepts_nested_attributes_for :auteurs
validates :titre, presence: true
CONTROLLER
(full text and I didn't translate in order to avoid typos)
def new
#biblio = Biblio.new(params_biblio)
#biblio.auteurs.build
end
def nouveau
# this method renders 'nouveau.html.erb',
# that contains the form allowing the addition of bibliography
#biblio = Biblio.new
if params[:id] # id is an optional parameter
#auteur = Auteur.find(params[:id])
#idauteur = #auteur.id
end
end
def ajouter
# is the method that treats the post form that was sent
#biblio = Biblio.new
if #biblio.save
# the 4 following lines are irrelevant here as they only add the
# second and subsequent authors to the join table.
# No validation and works fine.
b = auteurs_devises(params[:biblio][:auteurs])
aut = b.map do |var|
lett = Auteur.find(var)
lett.biblios << #biblio
end
redirect_to voir_biblio_url(Biblio.last)
else
if params[:id]
#auteur = Auteur.find(params[:id])
#idauteur = #auteur.id
end
render 'nouveau'
end
end
THE VIEW:
<%= form_for :biblio, url: administration_ajoute_biblio_url do |f| %>
<%= f.fields_for :auteurs do |aut| %>
<%= aut.label t('auteur') %>
<%= aut.text_field :nom , :name =>"biblio[auteurs][nom]", data: {autocomplete_source: auteurs_enum_path} %>
<% end %>
<%= f.label t('titre').capitalize %>
<%= f.text_field :titre %>
These are the params that are sent to the method nouveau:
Started POST "/administration/biblios/nouveau" for ::1 at 2017-02-07 21:28:28 +0100
Processing by Administration::BibliosController#ajouter as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+354h4M0Tg+BX21XAuQ6YMKS0BGQ8UjET8paKjkGCBsS1up1lB131KsoaCy563X4juDz0EJy46WgXbHcu51Kgw==", "biblio"=>{"auteurs"=>{"nom"=>"Godding"}, "titre"=>"Test Tuesday Evening", "soustitre"=>"", "recueil"=>"", "editeur"=>"", "annee"=>"", "isbn"=>"", "genre"=>"source", "revue_id"=>"", "revue_no"=>"", "revue_page"=>"", "lieu"=>"", "commentaire"=>""}, "commit"=>"Enregistrer"}
(0.2ms) BEGIN
(0.1ms) ROLLBACK
Rendering administration/biblios/nouveau.html.erb within layouts/messources
CONTROLLER AGAIN
def params_biblio
params.require(:biblio).permit(
:titre,
:soustitre,
:editeur,
:isbn,
:recueil,
:genre,
:revue_id,
:revue_no,
:revue_page,
:annee,
:lieu,
:commentaire,
auteurs: [:nom] )
end
For the sake of completeness, here's my routes.rb:
# ADMINISTRATION => BIBLIOGRAPHIE
get 'biblios/nouveau(/:id)' => 'biblios#nouveau', as: 'nouvelle_biblio'
post 'biblios/nouveau(/:id)' => 'biblios#ajouter', as: 'ajoute_biblio'
delete 'biblios/supprime/:id' => 'biblios#supprime', as: 'supprime_biblio'
get 'biblios/maj/:id' => 'biblios#cherche_maj', as: 'maj_biblio'
patch 'biblios/maj/:id' => 'biblios#maj', as: 'patch_maj_biblio'
I must be blind. I'm doing something wrong... I put a title to this bibliography ('Test Tuesday Evening'), this is the only field on which I left a validation, and despite this, validation always fails.

Saving arrays in Rails 4.2.3

I am having some trouble saving arrays in Rails.
Rails version: 4.2.3 | Ruby version: 2.2.1 | DB: PostgreSQL
In my view, I have a collection of check boxes that shows the conferences that my member attended.
<%= f.fields_for :conferences_member do |conference| %>
<%= collection_check_boxes(:conferences_member, :conference_id, #all_conferences, :id, :name)%>
<% end %>
I put a break point (binding.pry) after the create action in my MembersController, and surprisingly, it shows the selected check boxes:
Processing by Database::MembersController#create as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>"XYZ==",
[...] "conferences_member"=> {"conference_id"=>["3", "5", ""]}, [...]
Now, if I go to rails c, and type ConferencesMember.last to check what was saved, I get:
pry(main)> ConferencesMember.last
ConferencesMember Load (0.5ms) SELECT "conferences_members".* FROM
"conferences_members" ORDER BY "conferences_members"."id" DESC LIMIT 1
=> nil
These are my associations:
#=> member.rb
has_one :conferences_member
accepts_nested_attributes_for :conferences_member, allow_destroy: true, reject_if: :all_blank
#=> conferences_member.rb
serialize :conference_id, Array
belongs_to :member
#=> members_controller.rb
params.require(:member).permit( [...]
:conference_member_attributes => [:id, :member_id, :conference_id => []],
[...])
I want to thank you in advance. I've tried almost everything here on StackOverflow, but I don't see my error.
Thank you again.
EDIT:
More of my MembersController:
def new
#member = Member.new
#member.build_conferences_member
end
def create
#member = Member.new(member_params)
binding.pry
end
The log doesn't show any error, it just shows that conferences were not saved at all.
First, your field needs to be renamed to nest the :conference_id in :conferences_member_attributes (not in :conferences_member as you do now). Take advantage of the form object yielded by fields_for:
<%= f.fields_for :conferences_member do |conference| %>
<%= conference.collection_check_boxes :conference_id, #all_conferences, :id, :name %>
<% end %>
You also need to actually save the record in the create action: Member.new builds the record but does not save it. Typically, the create action branches based on whether the record saved or did not (due to validations). So you might rewrite this method like so:
def create
#member = Member.new(member_params)
# when #member.save returns true, it saved to the db successfully
if #member.save
redirect_to members_path, notice: "Member #{#member.id} saved!"
# otherwise, it didn't save because of a validation error, so we render the error
# to the user and give them a chance to fix it
else
flash[:error] = "Member didn't save: #{#member.errors.full_messages.to_sentence}"
render :new
end
end
Lastly, to make sure your data gets through your strong parameters, check your logs for any messages that parameters were filtered out. The messages look like:
Unpermitted parameters: your_favorite_attribute

Working with Rails arrays in Postgres

I have a postgres column called content which is an array.
But when trying to use it in a form I'm getting:
can't cast ActionController::Parameters to text
Despite the fact that the output looks pretty good:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"NkK4BggxknfEn0A8shTs06xmesERaZdYtZdl9oEEUTk=",
"notification_template"=>{"content"=>{"0"=>"Join us {{event_time}} {{{twitter_name}}} to win Big! hint: {{{question}}} #quiz {{location_tags}} {{url}} sdfsdfsdf"}},
"commit"=>"Update Notification template",
"id"=>"25"}
strong params
params.require(:notification_template).permit(:name, :content => {})
routes
resources :notification_templates do
get 'edit/:id', to: 'notification_templates#edit_content', as: 'edit_content'
end
controller
def edit_content
#notification_template = NotificationTemplate.find(params[:notification_template_id])
end
def update
if #notification_template.update(notification_template_params)
redirect_to admin_notification_template_path(#notification_template), notice: 'Social message was successfully updated.'
else
render action: 'edit'
end
end
my form
the url looks like: /notification_templates/25/edit_content/7 # custom action, but uses normal update
<%= simple_form_for([:admin, #notification_template]) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.simple_fields_for :content do |fields| %>
<%= fields.input params[:id], input_html: { value: #notification_template.content[params[:id].to_i] } %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
the DB column
add_column :notification_templates, :content, :text, array: true, default: []
Lastly, I was unsure about the conventions for adding it. The above worked fine, but I also noticed other possibilities such as
add_column :notification_templates, :content, :text, array: true, default: []
add_column :notification_templates, :content, :sting, array: true, default: []
add_column :notification_templates, :content, :text, array: true, default: {}
I choose the first one on the basis that a string wouldn't allow for as many characters as I might eventually need and text is more convenient. Also the default of [] vs {} or '{}'
But in postgres is see content text[] DEFAULT '{}'::text[]
log
Started PATCH "/admin/notification_templates/25" for 127.0.0.1 at 2014-11-28 14:25:43 +0100
Processing by Admin::NotificationTemplatesController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"NkK4BggxknfEn0A8shTs06xmesERaZdYtZdl9oEEUTk=", "notification_template"=>{"content"=>{"4"=>"{{{question}}} Study up and stop by {{{twitter_name}}} {{event_time}} for a #quiz {{location_tags}} {{url}} sdfsdfsdf"}}, "commit"=>"Update Notification template", "id"=>"25"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
NotificationTemplate Load (0.5ms) SELECT "notification_templates".* FROM "notification_templates" WHERE "notification_templates"."id" = $1 LIMIT 1 [["id", 25]]
(0.3ms) BEGIN
(0.3ms) ROLLBACK
Completed 500 Internal Server Error in 54ms
Reporting exception: can't cast ActionController::Parameters to text
TypeError (can't cast ActionController::Parameters to text):
app/controllers/admin/notification_templates_controller.rb:40:in `update'
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.1ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.4ms)
Rendered /Users/holden/.rvm/gems/ruby-2.0.0-p481#questionone-2.0/gems/actionpack-4.1.7/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (27.5ms)
UPDATE
I also observed that update array type field doesn't work as expected in the console.
eg. if I attempt to update a member of the array, something = record.content[2] = 'blah' it appears to work. But when I save the record it doesn't update it.
Yeah, Rails postgres Arrays are still a bit wonky. Hstore is a bit easier.
You may be better served going thru a virtual attribute and doing what you want expressly rather than relying on standard rails behavior through a form.
eg.
def content_member=(member)
unless member.blank?
self.content_will_change!
self.content[member.keys.first.to_i] = member.values.first
end
end
You also need to let rails know if you're going to update a member of an array, that's why it doesn't work in the console.
There's a full explanation here:
Rails 4 Postgresql array data-type: updating values

Rails test for contents in a form

I have a Comment form that also contains an Attachment form.
Comment model contains:
accepts_nested_attributes_for :attachments
Comment form contains:
<%= f.fields_for :attachments do |builder| %>
<%= builder.input :name, :label => 'Attachment Name' %>
<%= builder.file_field :attach %>
<% end %>
Comment Controller contains:
def new
#comment = Comment.new
#comment.attachments.build
If the user adds an Attachement, everything works fine.
I would like the user to be able to submit a Comment with or without an Attachment.
Right now, if the user enters a Comment without an attachment, the form re-displays and the Comment does not get created.
This is the log if I try to post a new Comment without an Attachement:
Started POST "/comments" for 127.0.0.1 at 2013-12-19 10:34:31 -0700
Processing by CommentsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"A6MOeMgoGUDmGiJr9PWinHVTAa7X63fgtA7+2my0A2Y=", "comment"=>{"user_id"=>"1", "status_date"=>"2013-12-19", "worequest_id"=>"10", "statuscode_id"=>"", "comments"=>"test", "attachments_attributes"=>{"0"=>{"name"=>""}}}, "_wysihtml5_mode"=>"1", "commit"=>"Save Comment"}
Tenant Load (0.3ms) SELECT "tenants".* FROM "tenants" WHERE "tenants"."subdomain" = 'ame' LIMIT 1
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."tenant_id" = 1 AND "users"."id" = 1 LIMIT 1
(0.1ms) BEGIN
(0.1ms) ROLLBACK
I need to figure out the right code so that the Attachment fields show up in the form, but the Comment will get created if no Attachment is selected.
Maybe I need to put code in the Attachment controller?
You could use Rails present? method to check if the object is not blank:
#comment.attachments.build if #comment.attachments.present?
I changed the Comment model to this:
accepts_nested_attributes_for :attachments, :reject_if => lambda { |a| a[:attach].blank? }, :allow_destroy => true

Rails form to create model with reference

I'm trying to create a model form that will allow me to add references by name via a <select> tag.
e.g.
In the database there are already RefModels with ref_name = "a", ref_name = "b", ref_name = "c".
Form:
<%= form_for #model %>
<%= f.label :ref_models, "Referenced Models:" %>
<%= f.select :ref_models, ["a","b","c"], {}, {:multiple => true} %>
Controller:
def create
#model = Model.new(model_params)
params[:model][:ref_models].each do |ref|
#ref = RefModel.where("ref_name = ?", ref)
#model.ref_models << #ref
end
respond_to do |format|
...
end
end
In my logs I'm getting this error:
Started POST "/models" for 127.0.0.1 at 2013-06-25 16:20:48 -0300
Processing by ModelssController#create as JS
Parameters: {"utf8"=>"✓", "models"=>{..., "ref_models"=>["a", "b", "c"], ...}, "commit"=>"Create"}
Completed 500 Internal Server Error in 2ms
ActiveRecord::AssociationTypeMismatch (RefModel(#70144007274440) expected, got String(#70144005442620)):
app/controllers/models_controller.rb:52:in `create'
What's the problem?
Note:
I changed the actual model names for "model" and "ref_model" to generalize.
EDIT:
The error occurs on this line:
#model = Model.new(model_params)
The error comes from this part:
#model = Model.new(model_params)
I'm pretty sure your model_params is actually params[:model]. If yes, it means it tries to create a record for Model with the attribute ref_models and the values contained in params[:model][:ref_models]
You should take off the params[:model][:ref_models] before passing it to the Model.new(params[:model]):
def create
ref_models = params[:model].delete(:ref_models)
#model = Model.new(params[:model])
ref_models.each do |ref|
#ref = RefModel.where("ref_name = ?", ref)
#model.ref_models << #ref
end
respond_to do |format|
...
end
end
Do these ref_models have IDs as well as names? If so, you can get Rails to do the heavy lifting for you.
In your view, do:
<%= f.select :ref_model_ids, RefModel.all.collect {|x| [x.name, x.id]}, {}, :multiple => true %>
Then, you can take out the custom code from your controller, Rails knows how to link those models up appropriately. That's assuming you have a table like model_ref_model with columns for model_id and ref_model_id.
Hope that helps...

Resources