My form partial is as follows:
<%= form_with(model: guitar, local: true) do |form| % >
<% if guitar.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(guitar.errors.count, "error") %> prohibited this
guitar from being saved:</h2>
<ul>
<% guitar.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<div class="form-group">
<%= form.label :title %>
<%= form.text_field :title, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_area :description, class: "form-control" %>
</div>
<div class="form-group">
<%= form.submit "Create New Guitar Lesson", class: "btn btn-primary"
%>
</div>
<% end %>
When I try to create a new entry in a table via a form, an error message pops up 'user must exist', which is fine by me.
How do I auto insert (which file / section should I place the code in) current logged in user ID to the form in the background without having the user keying it in manually?
I've pushed the entire app up to the cloud at http://github.com/cheese1884/197451 –
Assuming your field in table is called user_id and you are using Devise.
You should insert the following in your form
<%= form.hidden_field :user_id, value: current_user.id %>
The user's id (taken from Devise's current_user) will be prepopulated in a hidden field that they won't be able to see.
As per doc mentioned of user model
app/models/guitar.rb
class Guitar < ApplicationRecord
belongs_to :user
end
app/models/user.rb
class User < ApplicationRecord
has_many :guitars
end
In rails 5 belongs_to association required by default
which means at the time of creation of each record of guitar user_id is required.
So here by at controller you can solve it by: -
In devise after sign_in there is a helper method which current_user which is current logged_in user
in guitars_controller.rb
def create
new_guitar_record = current_user.guitars.new(guitar_params)
if new_guitar_record.save
#guitar created successfully for current logged in user
else
#current_user.guitar.errors.full_messages
end
end
def guitar_params
params.require(:guitar).permit(:name, :description)
end
Please amend controllers/application_controller as you have messed up devise's current_user and your custom current_user
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
end
Note: - You can also pass current_user.id by hidden_field :user_id with guitar form but as per security concern it's not good, as user can invoke any user_id via browser.
There are many way to implement this, as i understand you have an user_id field in Guitar model.
Simple solution is need to attach user_id into Guitar object on create action.
In GuitarController go for create and add this line. .merge(user_id: current_user.id).
Remember User must be signed in to get current_user object.
Sample:
#g = Guitar.new(guitar_params.merge(user_id: current_user.id))
Edited
You have many bugs there, 1st of all you need to clean up your controllers.
ApplicationController: remove lines between 6-18. No need it, because Devise gem will provide you these features already.
GuitarsController:
def guitar_params
params.require(:guitar).permit(:name, :description)
end
// guitar view from
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: "form-control" %>
</div>
// models/user.rb
//add this line
has_many :guitars
If you want to sign in a user in the background, use the sign_in helper inside your controller's action:
sign_in(:user, user)
Related
I've been working on introducing a nested form in my app following Railscast episode 196 http://railscasts.com/episodes/196-nested-model-form-revised and the remastered version for rails 4 https://github.com/dnewkerk/nested-model-form.
Let's say we have a 1-to-many association between receipts and articles.
Here's how their models look like:
receipt.rb:
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: true, reject_if: :all_blank
belongs_to :shop
belongs_to :user
def display_name
self.name
end
end
article.rb:
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
end
Here's how the receipts_controller.rb looks like:
class ReceiptsController < ApplicationController
before_action :set_shop, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
#receipts = current_user.receipts
respond_with(#receipts)
end
def show
respond_with(#receipt)
end
def new
#receipt = Receipt.new
2.times do
#receipt.articles.build
end
respond_with(#receipt)
end
def edit
end
def create
#receipt = Receipt.new(receipt_params)
user_id = current_user.id
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
#receipt.user_id = user_id
#receipt.save
respond_with(#receipt)
end
def update
if #receipt.update(receipt_params)
redirect_to #receipt, notice: "Successfully updated receipt."
else
render :edit
end
end
def destroy
#receipt.destroy
respond_with(#receipt)
end
private
def set_shop
#receipt = Receipt.find(params[:id])
end
def receipt_params
params.require(:receipt).permit(:name, :shopping_date, :shop_id, :file,
articles_attributes: [:id, :name, :brand, :warranty_time, :warranty_expires,
:receipt_id, :_destroy])
end
end
Here's how my receipts.js.coffee looks like:
jQuery ->
$('#receipt_shopping_date').datepicker(dateFormat: 'yy-mm-dd')
$.datepicker.setDefaults($.datepicker.regional['PL']);
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
$(document).ready(jQuery)
$(document).on('page:load', jQuery)
And finally here's my view for adding a new receipt and adding articles to it:
(other fields...)
<div class="large-12 columns">
<p>Add articles on the receipt:</p>
</div>
<div class="field">
<div class="large-12 columns">
<%= f.fields_for :articles do |builder| %>
<div class="article_fields">
<%= render "article_fields", :f => builder %>
</div>
<% end %>
<%= link_to_add_fields "Add another article", f, :articles %>
</div>
</div>
<div class="actions">
<div class="large-12 columns">
<%= f.submit "Sumbit Receipt" %>
</div>
</div>
<% end %>
As you can see I'm using a link_to_add_fields helper method, here's how it looks like:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields small button", data: {id: id, fields: fields.gsub("\n", "")})
end
And finally as you can see I'm generating a partial called _article_fields.html.erb, here's how it looks like:
<fieldset style="width:1400px">
<legend>new article</legend>
<div class="large-2 columns">
<%= f.text_field :name%>
</div>
<div class="large-2 columns">
<%= f.text_field :brand%>
</div>
<div class="large-2 columns">
<%= f.text_field :warranty_time, class: "warranty" %>
</div>
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
</fieldset>
Now let's get down to my problem. When creating a receipt for the first time everything is fine - I see the number of articles in a receipt in my show view and the warranty_expires in every article.
Things get messed up when I'm updating or deleting article_fields through receipts/edit:
1) When I edit a receipt and want to remove any of the articles (although visually in my edit view they disappear - the JS seems to work), the fields are not removed from my DB, thus the show view remains exactly the same like it was before.
Simple example:
before edit: my receipt has 6 articles
during edit: pressed 3 times the 'delete article' button, so the receipt should have 3 articles
after edit: the receipt has still 6 articles
2) When I edit a receipt and want to add an another article field, the value warranty_expires is always nil - how can I make it work with the update action in my receipts controller? I tried using the same code as in my create action:
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
but it won't work. Any idea why?
Simple example:
A receipt has 2 articles already. When I add the 3rd one I get the following result:
3 articles - all of them have names and warranty_time fields, but only 2 of them have a warranty_expires value.
All of your help would be deeply appreciated. Thank you in advance.
I think you can use some callbacks in your Article model for solve your 2nd problem,
Start deleting this, try to keep your controller as simple as possible and handle the operations in your models.
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
In your Article Model add some callbacks
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
before_update :set_warranty_expires
before_create :set_warranty_expires
def set_warranty_expires
self.warranty_expires = self.receipt.shopping_date.advance(months: self.warranty_time)
end
end
Code its not tested, but its the idea. Hope it helps.
Check this two gems simple_form and nested_form this helps a lot when writing large forms and they play well with each other.
Update: I managed to fix the 1st issue.
The fix for the 1st solution is the following:
the hidden field :_destroy was missing for removing articles.
So I needed to changed the following code:
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
to:
<div class="large-12 columns">
<%= f.hidden_field :_destroy %>
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
Still have no idea how to fix the 2nd issue though.
First of all i noticed that, you have a loop in your reciepts_controller new action
2.times do
#receipt.articles.build
end
This means that, article will be created for only 2 times for that reciept.
Better remove the loop so that you can add as many as article you want.
For Issue number two add bellow line to edit action to your controller
#receipt.articles.build
I guess that would help you.
Also nested_form is a great gem for manage this kind of tasks.
https://github.com/ryanb/nested_form
Check it out.
This is an issue where .hide() is called in receipts.js.coffee. The simplest way to fix this I can think of is to simply replace .hide() with .remove()
I created a sample webpage in rubyonrails which has two textbox and a button . When i enter some data in the text box and click the button no error appears . But the data is not stored in the data base . What is the mistake that i committed .
login.html.erb file :
<%= form_for #product, url:{action: "login"} do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :username %><br>
<%= f.text_field :username %>
</div>
<div class="field">
<%= f.label :password %><br>
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
routes.rb file:
Sample::Application.routes.draw do
get "sample/login"
root 'sample#login'
post 'sample/:id' => 'sample#login'
end
sample controller file :
class SampleController < ApplicationController
def login
#product=Logz.new
end
end
and the model class name Logz contains the necessary field names
username and password . If there was any error i could manage . but it shows no errors.
I had the same problem once. I dont know how its happened . but i just changed the content in database.yml
localhost:yourdb_development
into
127.0.0.1:ypurdb_development
and i got it working.
You are sending the data to method login, but it just instantiate a new Product and it aren't receiving any attributes... and even if received... it are not saving the Product at all, so does not persist the data.
Try add a method create... that will be responsible for receive the data and save
class SampleController < ApplicationController
def create
#product = Logz.new(params[:product])
if #product.save
format.html { redirect_to 'sample#login', notice: 'Data saved successfully' }
else
flash[:notice] = 'A wild error appeared'
end
end
After that, create the route to post 'sample/create' and change the action to where your form send the data... form_for #product, action: "create", method: 'post'
Doing that... i will be possible to persist the data on your database...
PS:
You can use the content of that method inside your login method... but I dont recommend that... it is ugly and does not follos the conventions of rails.
I even recommend you to do a refactory... because it doesn't make sense access a SampleController in order to create a Product... that is persisted in an object called Logz...
The best practice is all follow the same name... LogzController, #logz, and finally your model Logz. and preferably your routes following the same pattern...
Another thing is, it would be nice to change your method login to a method call 'new' because that method you use to fill a new Logz... not to login...
I currently have a simple app that includes user authentication through devise and a message model(the message model uses Jquery and Faye). Both are working fine independently, but I would like to add a user_id to the messages.
I have already updated the schema and models with the relationship, but I am having trouble inputting the necessary code to have the view and controller input the relationship into the db, while keeping the jquery working. I wasn't sure of the best way, but here I tried to create a hidden field that would pull the user_id, not sure if this is even possible. Here is the applicable code, any help is appreciated.
Message index.html.erb
<ul id="chat">
<%= render #messages %>
</ul>
<%= form_for Message.new, :remote => true do |f| %>
<div class="field">
<%= f.text_field :content %>
</div>
<div class="field">
<%= f.hidden_field :user_id %>
</div>
<div class="actions">
<%= f.submit "Send" %>
</div>
<% end %>
create.js.erb for messages
<% broadcast "/messages" do %>
$("#chat").append("<%= escape_javascript render(#user.message) %>");
<% end %>
$("#new_message")[0].reset();
Messages Controller
class MessagesController < ApplicationController
def index
if #authentications = current_user
#messages = Message.all
else
redirect_to authentications_url
flash[:notice] = "You need to sign in before answering questions"
end
end
def create
#user = User.find(params[:user_id])
#message = #user.message.create(params[:message])
end
end
I think you have everything you would need, but if not, let me know and I will be happy to provide it for you.
Thanks, everyone.
two things to correct,
1)use user association to create message instance in form(probably current_user if logged-in user create a message)
<%= form_for user.messages.new, :remote => true do |f| %> #// assuming its has many association
2) if it is has_many association then change association in create action
#message = #user.messages.create(params[:message])
I have a parent model that accepts child attributes.
class User < ActiveRecord::Base
accepts_nested_attributes_for :spec
attr_accessible :name, :spec_attributes
In the view I have a form that gets information for 3 models. I use a generic form_tag.
<% form_tag(action) do %>
.
.
.
<% fields_for "user[spec_attributes]" do |spec_form|%>
<%= spec_form.check_box :alert_greeting %>
<%= spec_form.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
In the Controller
#user = User.find(session[:user_id])
if #user.update_attributes(params[:user])
do something.
end
The database is getting updated and the all seems to be working.
However when I go back to the form to edit again, even though the value for the checkbox is showing 1 the check box is not checked.
Any ideas as to how to show the checkbox as being checked when it is supposed to be?
Thanks a lot in advance.
You need to reference the specific spec record that is within the user you're calling. Try changing
<% fields_for "user[spec_attributes]" do |spec_form|%>
to
<% fields_for #user.spec do |spec_form|%>
You'll need to make sure that you have a non-nil spec object built for the user (but not necessarily saved) in your edit controller action.
You can do attributes and nested attributes using two fields_for calls like this:
<%= form_tag(action) do %>
... other form tags ...
<%= fields_for :user, #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.fields_for :spec do |s| %>
<%= s.check_box :alert_greeting %>
<%= s.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
<% end %>
I'm writing a (text) messaging app with Rails. I'm using nested_forms to allow you to send a message to multiple people.
In the controller, I instantiate a new Message object, then for each Member, build a Recipient object (child of Message). In the form, I display a checkbox next to each recipient. I want it so that the new Message object only has the recipients that have checks next to them. This is not working.
So by the time the form is rendered, Recipient objects are instantiated for all members. In other words, by default, a message gets sent to each member, unless specified not to. But I want to use the form to allow the user to specify who he wants the messages sent to
Here are my models:
class Message < ActiveRecord::Base
has_many :recipients
accepts_nested_attributes_for :recipients
#columns: body:string, from:string, from_member_id:integer
end
class Member < ActiveRecord::Base
#columns phone:string, name:string
end
class Recipient < ActiveRecord::Base
belongs_to :message
belongs_to :member
#columns: member_id:integer, message_id:integer
end
messages_controller.rb:
def new
#message = Message.new
#members = Member.all
#members.each do |member|
#message.recipients << Recipient.new(:member_id => member.id)
end
end
def create
#message = Message.new(params[:message])
redirect_to '/somewhere'
end
...
And here's my form for Message (app/views/message/new/html.erb)
<%= form_for(#message) do |f| %>
<% if #message.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#message.errors.count, "error") %> prohibited this message from being saved:</h2>
<ul>
<% #message.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :recipients do |builder| %>
<div class="field">
<input type="checkbox" value="<%= builder.object.member_id %>" name="recipients[id]" />
/*WHAT GOES ^^^HERE^^^? */
<%= builder.object.member.name %>
</div>
<% end %>
<div class="field">
<%= f.label :body %><br />
<%= f.text_field :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The commented line in the form is where I'm having trouble. Also, it seems I might need to modify some code in MessagesController#create, but I'm not sure where to start.
First, instead of writing your checkbox HTML by hand, you should use the Rails helpers for this. It'll save you a lot of work, particularly in redisplaying the form upon a validation failure.
To do this, you'll need to create an attribute on your Recipient class:
class Recipient
attr_accessor :selected
end
Then you can hook up that attribute to the checkbox:
<%= builder.check_box :selected %>
The next step is to make that attribute do something. You could try using the :reject_if option for accepts_nested_attributes_for. You could pass it a proc that returns true if the checkbox is not checked, e.g.:
accepts_nested_attributes_for :recipients, :reject_if => proc { |attributes| attributes['selected'] != '1' }
See these docs for details on :reject_if:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html