I added just this one piece of coding to app and now it doesn't allow anyone to register. I receive the following error: 1 error(s) on assignment of multiparameter attributes. The title of the error page is "ActiveRecord::MultiparameterAssignmentErrors in UsersController#create". I am not sure how to fix as other posts were not helpful unfortunately. Anyone have some solutions?
new.html.erb:
<div class="field">
<%= f.label :birthday %>
<%= f.date_select :birthday, :start_year => 1995, :end_year => 1930 %>
</div>
users_controller:
def create
#user = User.new(params[:user])
if #user.save
UserMailer.registration_confirmation(#user).deliver
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
development.log:
Started POST "/users" for 127.0.0.1 at 2013-03-22 10:27:31 -0400
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7KAgvcc6yvuhKQGNrJo8UpfsUyuNG16TuMsRj6qst48=", "user"=>{"email"=>"james#james.com", "password"=>"[FILTERED]", "username"=>"james", "zip_code"=>"84784", "gender"=>"women", "age"=>"23", "age_end"=>"39", "birthday(1i)"=>"1995", "birthday(2i)"=>"3", "birthday(3i)"=>"22", "role"=>"admin"}, "commit"=>"Create User"}
Completed 500 Internal Server Error in 100ms
ActiveRecord::MultiparameterAssignmentErrors (1 error(s) on assignment of multiparameter attributes):
app/controllers/users_controller.rb:18:in `new'
app/controllers/users_controller.rb:18:in `create'
The actual problem was the value in database was on VARCHAR, and instead it should be DATE. Now it works fine.
First make sure that the type of the birthday should be Date.
Here in parameters, the values for birthday is going like below.
"birthday(1i)"=>"1995", "birthday(2i)"=>"3", "birthday(3i)"=>"22"
But the type of the field id Date. So as i think, before updating the birthday field, we need to generate the appropriate date object and then need to update the object.
#user.birthday = Date.strptime("#{params['birthday(3i)']}/#{params['birthday(2i)']}/#{params['birthday(1i)']}", "%d/%m/%y")
Now save the object and hopefully it will not raise any error this time.
If still having error, pls let me know.
Related
My Controller
class MessagesController < ApplicationController
def index
#messages = Message.all
end
def new
#message = Message.new
end
def create
#message = Message.new(message_params)
if #message.save
redirect_to '/messages'
else
render 'new'
end
end
private
def message_params
params.require(:message).permit(:content)
end
end
My corresponding view
<div class="create">
<div class="container">
<%= form_for(#message) do |f| %>
<div class="field">
<%= f.label :message %><br>
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit "Create" %>
</div>
<% end %>
</div>
</div>
I'm a bit confused about how things work under the hood for Rails object creations. Currently doing the Codecademy tutorial, but they've skipped a couple of explanation steps.
When the form submits button is pressed does f.submit generate
a JSON object in a POST request?
After getting routed to the message controllers' create action. How does #message.save know if it's been saved successfully? Isn't it just an object populated by the parameters passed in at this point? Does it route to the DB first before the controller?
You can see what gets submitted by a form submission in your rails server logs. Just run rails server in a terminal, open up your localhost, submit a form and immediately check what gets spit out in the terminal.
You might get something like this:
Started POST "/messages" for ::1 at 2021-10-25 11:41:33 +0200
Processing by MessagesController#create as HTML Parameters: {"message"=>"text", "content"=>"hey"}
[here you will see the SQL run to INSERT new data into the database]
Completed 201 Created in 1ms (ActiveRecord: 2.0ms | Allocations: 2073)
Breaking this down you get 5 pieces of information.
Type of request and endpoint with a timestamp
Controller and format used
Params in JSON
SQL query run if any
Response status with benchmarks for parts used in response, how much time queries took, how much rendering took etc.
save method from rails will try to save an instance of an initialized model into the database and will return true or false depending on the result of the action. There is also save! method that will raise an error if the operation fails, instead of simply returning false boolean value. So to answer your question specifically:
JSON object is sent in params of your POST request, generated based on the HTML form.
#message is an object populated by params (in your case it's only content param that is actually used). Using save on it will prompt ActiveRecord to connect to the database and perform an INSERT action, saving it to the database.
It does not route to the DB first, the controller controls the actions performed based on the request. If something hits the database, you will have to prompt it, as you do by using the save method.
save method in rails documentation
After upgrading my app to Rails 5.2, I've found some time to look at Active Storage. Following the guide, I've installed it and ran the migrations necessary.
On my User model I want to attach an avatar as per the example here: Edge Guide for Active Storage
The error I am receiving upon submitting my form is ActionController::InvalidAuthenticityToken
<%= form_for #user, remote: true do |f| %>
<%# Other user fields redacted %>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar %>
</div>
<%= f.submit "Save", remote: true %>
<% end %>
I changed the form_for to include authenticity_token: true like this:
<%= form_for #user, remote: true, authenticity_token: true do |f| %>
This removed my authenticity error and inserted the file into my DB, however this has caused an Unknown format error, in that it is routing to my controller with html instead of js.
Logs:
Started PATCH "/users/22" for 127.0.0.1 at 2018-11-07 13:36:22 +0000
Processing by UsersController#update as HTML
Disk Storage (5.7ms) Uploaded file to key: aJQ3m2skk8zkHguqvhjV6tNk (checksum: 7w6T1YJX2LNIU9oPxG038w==)
ActiveStorage::Blob Create (23.6ms) INSERT INTO `active_storage_blobs` (`key`, `filename`, `content_type`, `metadata`, `byte_size`, `checksum`, `created_at`) VALUES ('aJQ3m2skk8zkHguqvhjV6tNk', 'Dq3gtJjU0AAbdIj.jpg-large.jpeg', 'image/jpeg', '{\"identified\":true}', 50642, '7w6T1YJX2LNIU9oPxG038w==', '2018-11-07 13:36:22')
ActiveStorage::Attachment Create (3.4ms) INSERT INTO `active_storage_attachments` (`name`, `record_type`, `record_id`, `blob_id`, `created_at`) VALUES ('avatar', 'User', 22, 1, '2018-11-07 13:36:22')
(9.4ms) ROLLBACK
Completed 406 Not Acceptable in 630ms (ActiveRecord: 93.1ms)
ActionController::UnknownFormat (ActionController::UnknownFormat):
Users#Update
def update
respond_to do |format|
if #user.update(user_params)
flash.now[:notice] = 'User saved successfully!'
format.js do
#users = User.all
end
else
flash.now[:alert] = #user.errors.full_messages.to_sentence
format.js do
#users = User.all
render layout: false, content_type: 'text/javascript'
end
end
end
end
Any ideas as to why it is being submitted as HTML instead of JS?
Edit: Form Markup
<form class="edit_user" id="edit_user_22" enctype="multipart/form-data" action="/users/22" accept-charset="UTF-8" data-remote="true" method="post">
After much trial & error, it took using the Direct Upload feature of Active Storage to allow this to work. Allow me to explain my findings:
remote: true, multipart: true don't play well together. See this stack overflow post for more details. Essentially you need to use jQuery or a gem to submit files remotely.
Following this edgeguides post (direct uploads). It seems as though when you click on submit; direct upload will catch the submit event and submit the file directly to a cloud server (or local in my dev case). It will then use the reference of that image in the form submit, instead of submitting the actual image.
This hit my Users#update using JS and successfully attached the Avatar.
Also transiting to Active Storage and stuck here.
But (I recalled that) in my previous implementation, where I used github/jQuery-File-Upload I solved (as it turns out) the same problem:
Rails.fire($("#form_id")[0], 'submit');
This is a rails-ujs method I got rom this Q/A: /with-rails-ujs-how-to-submit-a-remote-form-from-a-function
As I get now all it goes from the fact that:
The event — submit — is not raised when calling the form.submit() method directly, — MDN:
— https://developer.mozilla.org/en-US/docs/Web/Events/submit
— https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
— https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript
In my app, a micropost is simply called an "item". On each user's "show" page, there should be a form for making a new "item" that belongs to that user.
I am getting stuck making such a form.
When the form is submitted, the controller action below fires:
#user = User.find_by(name: params[:name])
#user.items.create(item_params)
redirect_to 'root'
By the way, the 'item_params' method is
params.require(:item).permit(:title, :content, :user_id)
I am getting an error about the second line. The error is
undefined method 'items' for nil:NilClass
I checked in the rails console whether I could make a new item by first selecting a user by name
#user = User.find_by(name: "tester1")
then create an item for it with the below statement:
#user.items.create(title: "new_item_title", content: "new_item_content")
and the new item was created as expected. I cannot explain the browser error, though.
EDIT
The form in question is on a "show_user". Here is the log for the HTTP request for that page:
Started GET "/center/show_user?utf8=%E2%9C%93&name=tester&commit=Search" for 12
.0.0.1 at 2014-03-19 11:46:51 -0700
Processing by CenterController#show_user as HTML
Parameters: {"utf8"=>"√", "name"=>"tester", "commit"=>"Search"}
←[1m←[35mUser Load (1.0ms)←[0m SELECT "users".* FROM "users" WHERE "users"."
ame" = 'tester' LIMIT 1
Rendered center/show_user.html.erb within layouts/application (2.0ms)
Completed 200 OK in 23ms (Views: 18.7ms | ActiveRecord: 1.0ms)
The form is hosted on this page. Here is the request that is sent with submission of the form:
Started POST "/center/show_user" for 127.0.0.1 at 2014-03-19 11:47:14 -0700
Processing by CenterController#create_item_owned as HTML
Parameters: {"utf8"=>"√", "authenticity_token"=>"P6mb63rhTHx6Q17zhfu4WXErG5Pb0
8t0d/L1RppKAl0=", "item"=>{"title"=>"uno", "content"=>"uno1"}, "commit"=>"Save I
tem"}
←[1m←[36mUser Load (1.0ms)←[0m ←[1mSELECT "users".* FROM "users" WHERE "users
"."name" IS NULL LIMIT 1←[0m
Completed 500 Internal Server Error in 4ms
NoMethodError (undefined method `items' for nil:NilClass):
app/controllers/center_controller.rb:18:in `create_item_owned'
So no information about the user is being passed along with the parameters when the form is submitted. How how I fix this?
Here are my relevant routes:
get "center/show_user/" => 'center#show_user'
post "center/show_user" => 'center#create_item_owned'
The "create_item_owned" action is the one described at the beginning of this question.
The "show_user" action is
#user = User.find_by(name: params[:name])
#new_item = Item.new
How should I forward along those parameters in such a way that the new object created by the form is automatically associated with the specific user in the database (along with automatic creation of foreign keys)?
My form is
<%= form_for :item do |x| %>
Title: <%= x.text_field :title %> <br>
Content: <%= x.text_field :content %> <br>
<%= x.submit %>
<% end %>
I think you might still have to find by id rather than by name. The name might not be passed in the params. You should check your logs to see what params are being sent.
I've searched and searched and nothing came up, so I need to ask you guys for help.
I have a simple new form that should take two fields from form_for and then post it into database. The problem is it doesn't.
My sample db has only two fields: name and site_id
Here's my new.html.erb:
<%= form_for #kejsu do |f| %>
<%= f.text_field :name %>
<%= f.text_field :site_id %>
<%= f.submit "Create" %>
<% end %>
Here's the controller:
def new
#kejsu = Kejs.new
end
def create
#kejsu = Kejs.new(params[:kejsu])
if #kejsu.save
redirect_to kejs_index_path
else
render "new"
end
end
After hitting submit button only timestamps are inserted. Here's the snippet from rails server:
Started POST "/cases" for 192.168.56.1 at 2013-12-10 23:11:03 +0000
Processing by KejsyController#create as HTML
Parameters: {"utf8"=>"â", "authenticity_token"=>"Sr2ssiwtRtk9pRT5VfuDFglsEmGnjzwVkRGGBSb2zhA=", "kejs"=> {"name"=>"aa", "site_id"=>"3"}, "commit"=>"Create"}
(0.1ms) begin transaction
SQL (5.1ms) INSERT INTO "kejs" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Tue, 10 Dec 2013 23:11:03 UTC +00:00], ["updated_at", Tue, 10 Dec 2013 23:11:03 UTC +00:00]]
(4.0ms) commit transaction
Redirected to http://192.168.56.101:3000/cases
Completed 302 Found in 14ms (ActiveRecord: 9.2ms)
As you can see those fields are passed as parameters, but INSERT doesn't insert them at all.
I've tried it with default restful routing, writing my own routes and it doesn't work either way.
As a bonus my routes:
get 'cases' => 'kejsy#index', as: :kejs_index
get 'cases/new' => 'kejsy#new', as: :new_kejs
post 'cases' => 'kejsy#create'
Your params[:kejsu] reference uses :kejsu, but your parameters exist under params[:kejs], so you're picking up nil for the value.
The key in params is determined by the class name of the object passed to form_for (i.e. Kejs in this case), not by the variable name (i.e. #kejsu). If you think about it, this makes sense because the variable name is not even available to form_for, since the value of the #kejsu is what is passed.
to debug you can use render :text => params.inspect inside your controller so I will change my code like this
def create
render :text => params.inspect
end
Now go and sumbit your form. What is name of key inside your params? Is it kejsu or kejs?
If its kejs than you can change your code to
def create
#kejsu = Kejs.new(params[:kejs])
if #kejsu.save
redirect_to kejs_index_path
else
render "new"
end
end
As promised, the answer with strong parameters:
def create
#kejsu = Kejs.new(kejs_params)
if #kejsu.save
redirect_to kejs_index_path
else
render "new"
end
end
def kejs_params
params.require(:kejs).permit(:name, :site_id)
end
I needed to change the way I'm dealing with parameters passed via form by creating new method that permits given fields. Now it works like it supposed to.
Thanks for all the hints.
I'm trying to use accepts_nested_attributes_for on a has_one association model, and getting absolutely nowhere :-(
I have two models, a user and a location. A user has one location:
class User < ActiveRecord::Base
# current location
has_one :location, :dependent => :destroy
accepts_nested_attributes_for :location
end
class Location < ActiveRecord::Base
belongs_to :user
end
I can save changes to the model by using User.find(1).location.current_location_text = "blah" from the console, so I know the associations are set up correctly.
I have two forms on the edit user page. One that updates the main user attributes (and works fine and not shown below) and then this one that allows the user to update an attribute of the location model, called "current_location_text":
<%= form_for(#user) do |f| %>
<%= fields_for(#user.location) do |location_fields| %>
<%= location_fields.label :current_location_text, 'Current Location' %>
<%= location_fields.text_field :current_location_text, :placeholder => 'Road, City or Postcode' %>
<% end %>
<%= f.submit "Update Current Location" %>
<% end %>
This doesn't work. I'm slightly confused as the params sent by the form look incorrect. When the form is submitted, this is in the log:
Started PUT "/users/1" for 127.0.0.1 at 2011-10-08 00:28:05 +0100
Processing by UsersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"YdTAsXwEvRgXIqri+jfx3dLlYG2XWQTuYkgLDsO/OJw=", "location"=>{"current_location_text"=>"E14 8JS"}, "commit"=>"Update Current Location", "id"=>"1"}
User Load (10.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (5.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = ? LIMIT 1 [["id", "1"]]
SQL (4.4ms) BEGIN
(2.5ms) COMMIT
Redirected to http://localhost:3000/users/1
Two things that I find bizarre about this:
There's the "COMMIT" message, but with no preceding update string, and no error. eg, if you tried to commit a protected attributed, you'd get the "you can't mass assign..." error message at that point.
The params look wrong to me. The "location" bit is nested as I'd expect, but I'd also expect this to be a nested within the "user" hash, something like this:
{"utf8"=>"✓", "authenticity_token"=>"YdTAsXwEvRgXIqri+jfx3dLlYG2XWQTuYkgLDsO/OJw=", "user"=>{"location"=>{"current_location_text"=>"E14 8JS"}, "commit"=>"Update Current Location", "id"=>"1"}}
I don't think I'm being completely stupid here. Am I missing something really obvious? I've tried adding extra hidden fields to my form, ie a user id, and then I get the user hash, but at the same level as the "location" hash, and not as a parent of it as I'd expect!
Also if it helps, here's my update within my UsersController:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to current_user, :notice => 'User was successfully updated.'
else
render :action => "edit"
end
end
and here's what's in my routes.rb (although I don't think it's relevant):
resources :users do
resource :location
end
Any help appreciated. If I don't solve this, the laptop is going out the window....
Thanks.
<%= fields_for(#user.location) do |location_fields| %>
This is your problem. You need to actually "nest" the fields_for inside your form, like this:
<% f.fields_for(#user.location) do |location_fields| -%>
Try this instead
<%= f.fields_for :location do |location_fields| %>
Rather than giving it the object itself, tell rails what association you want to have it load for