Hey guys, this has been plaguing me all week long. I am new to Rails, so please be gentle :)
My root problem is I'm trying to write a form_for Post that will use autocompletion on an input to tie the post to a Client. The database is looking for a client_id, not a text name.
So I have tried a custom validation that will lookup the text value and replace it with an id.
My Post.rb file has this:
class Post < ActiveRecord::Base
belongs_to :client
attr_accessor :client_name
attr_accessible :client_name
before_validation_on_create :validate_client
def validate_client
self.client_id = 1 # just a test
end
def client_exists(passed_name)
return Client.where(:full_name => passed_name).first.blank?
end
end
But when I do this, none of the form variables get passed. The database gets all blank entries except for the client_id. How can I accomplish this? Why aren't my form variables being passed in? Many thanks in advance.
Edit 1: added create definition from posts_controller.rb
def create
#post = Post.new(params[:post])
respond_to do |format|
if #post.save
format.html {
redirect_to(posts_url) # redirect_to(#post, :notice => 'Post was successfully created.')
}
format.xml { render :xml => #post, :status => :created, :location => #post }
format.js
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
Edit 2: Thanks to #apneadiving, I changed the attr_accesible to include the other attributes, and that passes the POST entries into the db.
attr_accessible :client_id, :time, :content, :client_name
But when I change the validate_client function to search for the client_id,
def validate_client
passed_name = self.client_name
if client_exists(passed_name)
c = Client.where(:full_name => passed_name).first
self.client_id = c.id
else
errors.add_to_base "Error"
end
end
It always gives me this error:
Called id for nil, which would
mistakenly be 4 -- if you really
wanted the id of nil, use object_id
Edit 3: Here's my post form. I can't get the value of :client_name properly.
<%= form_for(#post) do |f| %>
<%= render 'common/error_messages', :object => f.object %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :size => "50x6" %>
</div>
<div class="field">
<div class="sub-field">
<%= f.label :client_name %><br />
<%= f.text_field :client_name, :size => "26" %>
</div>
<div class="sub-field">
<%= f.label :date %><br />
<%= f.text_field(:time, :class => 'date', :size => "10", :value => Date.today.strftime('%m/%d/%Y')) %>
</div>
<div class="sub-field">
<%= f.label :time %><br />
<%= f.time_select :time %>
</div>
</div>
<div class="actions clear">
<%= f.submit %>
</div>
<% end %>
Edit 4: The solution. I was struggling to get the :client_name due to my text being too far tabbed in (I was originally privatizing the function and then took the word "private" out). A modified version of #apneadiving's answer solved it for me!
class Post < ActiveRecord::Base
belongs_to :client
attr_accessor :client_name
validate :validate_client
def validate_client
passed_name = self.client_name
unless client = Client.find_by_full_name(passed_name).blank?
self.client_id = Client.find_by_full_name(passed_name).id
else
errors.add(:client_name, "'#{passed_name}' cannot be found.")
end
end
end
Beware of your attr_acessible:
if you set one, you have to set all the variables that can be set through params. Otherwise they are protected against mass assignment
EDIT 1:
seems c.id is nil which means your client_exists function doesn't work as expected. Try the following (not tested):
def validate_client
unless client = Client.find_by_full_name(client_name).nil?
client_id = client.id
else
errors.add_to_base "Error"
end
end
Your client_exists code is wrong, it returns true if it doesn't exist.
Also, since your client_exists and the validation reuse the same query i would rewrite that a bit.
def validate_client
passed_name = self.client_name
existing_client = Client.where(:full_name => passed_name).first
if existing_client
self.client_id = existing_client.id
else
errors.add_to_base "Error"
end
end
Related
I've tried setting up a form validation that would ensure that at least 1 and at most 3 tags must be included in the form. But it isn't working as an empty form is still processed, but a form with 4 comma-seperated tags is validated correctly.
Controller
def update
#product = Product.find(params[:id])
current_user.tag(#product, :with => params[:product][:tag_list], :on => :tags)
if #product.update_attributes(params[:product])
redirect_to :root, :notice => "Added"
else
render :action => 'edit'
end
end
Form
<%= form_for #product do |f| %>
<%= f.label :tag_list, "Your tags" %> <%= f.text_field :tag_list, :value => #product.tags_from(current_user) %>
<p><%= f.submit "Change" %></p>
<%= f.error_messages %>
<% end %>
Model
validate :required_info
validates_size_of :tag_list,
:maximum => 3
private
def required_info
if( tag_list.empty? and description.empty? )
errors.add_to_base "Add one"
end
end
You could use a custom validation:
validates :tag_list_length
private
def tag_list_length
errors.add(:tag_list, "Must include at least one and no more than three tags") unless tag_list.length.between?(1,3)
end
if( tag_list.empty? and description.empty? )
errors.add_to_base "Add one"
end
Just looking at this part of the model, I think you'd rather do if(tag_list.empty? or description.empty?) because you want both of them to be filled.
For the second validation, I'm not an act_as_taggable user so I can't answer you now.
I have these models:
class User < ActiveRecord::Base
has_one :city
accepts_nested_attributes_for :city
end
class City < ActiveRecord::Base
belongs_to :user
end
This controller action:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
and this view:
<%= form_for :user,:url => users_path,:method => :post do |f| %>
<%= f.fields_for :city do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to allow the user to select a city from the list of already added cities. I am trying to present him a select. The select part it works, but the generated html code for it, looks like this:
<select name="user[city][id]" id="user_city_id">
<option value="1">One</option>
<option value="2">Two</option>
</select>
Notice that it's name doesn't have attribute anywhere. So, when I try to save it, I get this error:
City(#37815120) expected, got ActiveSupport::HashWithIndifferentAccess(#32969916)
How can I fix this?
EDIT: there is some progress, I tried to change the fields_for to this:
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
and now, the html seems to generate correctly. But I get this error now:
Couldn't find City with ID=1 for User with ID=
I have no idea what to do next.
EDIT2: overriding the city_attributes= method seems to work:
def city_attributes=(attribs)
self.city = City.find(attribs[:id])
end
I don't know if it's the way to go, but it seems good.
Have a look at this question that seems similar to yours :
Rails 3: How does "accepts_nested_attributes_for" work?
Actually, since the Cities already exsit, I think there is no need for nested forms here.
Try Replacing
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
With
<%= f.collection_select :city, City.all,:id,:name %>
Updated afters comments
Could you change your relationship with (and update database scheme accordingly)
class User < ActiveRecord::Base
belongs_to :city
end
class City < ActiveRecord::Base
has_many :users
end
And then try using:
<%= f.collection_select :city_id, City.all,:id,:name %>
You could also do a
<%= f.collection_select :city_id, City.all, :id, :name %>
in your view and then add virtual attributes to your User model:
class User < ActiveRecord::Base
...
def city_id(c_id)
update_attribute(:city, City.find(c_id))
end
def city_id
city.id
end
end
This might not be very clean, since the associated City model is "saved" whenever assigning an ID to some_user.city_id. However, this solution keeps your controller and view nice and clean.
Note: you might also want to account for a blank ID being passed in to the setter method.
Try this
<%= f.select(:city_id, City.all.collect {|p| [ p.name, p.id ] }) %>
I've get a little problem.
My controller:
def new
#company = Company.new
#title = "Create company"
end
def create
#company = Company.new(params[:company])
#company.admin_id = current_user.id
if #company.save
flash[:success] = "Company created!"
redirect_to admin_path
else
#title = "New company"
render 'new'
end
end
new.html.erb
<%= debug params[:company] %>
<% form_for #company, :html => { :multipart => true } do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<%= render 'fields', :f => f %>
<div class="actions">
<%= f.submit "Submit company!" %>
</div>
<% end %>
Company.rb model
validates :name,
:presence => true,
:length => { :maximum => 20 }
validates_attachment_presence :logo
But after submitting form I've get anyway only one error:
Name can't be blank
Of course I'm filling name and logo fields.
Any ideas? Thanks.
You didn't include your _fields partial, and that's probably where your problem is.
Make sure your inputs have appropriate name attributes.
Try to instantiate your model in console and see if validations really work.
You can try something like this: c = Company.new; c.valid?; c.errors and you'll see your errors hash in console.
Sorry for this question but I can't find my error!
In my Project I have my model called "team".
A User can create a "team" or a "contest". The difference between this both is, that contest requires more data than a normal team.
So I created the columns in my team table.
Well... I also created a new view called create_contest.html.erb :
<h1>New team content</h1>
<% form_for #team, :url => { :action => 'create_content' } do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<p>
<%= f.label :url %><br />
<%= f.text_fiels :url %>
</p>
<p>
<%= f.label :contact_name %><br />
<%= f.text_fiels :contact_name %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
In my teams_controller, I created following functions:
def new_contest
end
def create_contest
if #can_create
#team = Team.new(params[:team])
#team.user_id = current_user.id
respond_to do |format|
if #team.save
format.html { redirect_to(#team, :notice => 'Contest was successfully created.') }
format.xml { render :xml => #team, :status => :created, :location => #team }
else
format.html { render :action => "new" }
format.xml { render :xml => #team.errors, :status => :unprocessable_entity }
end
end
else
redirect_back_or_default('/')
end
end
Now, I want on my teams/new.html.erb a link to "new_contest.html.erb".
So I did:
<%= link_to 'click here for new contest!', new_contest_team_path %>
When I go to the /teams/new.html.erb page, I get following error:
undefined local variable or method `new_contest_team_path' for #<ActionView::Base:0x16fc4f7>
So I changed in my routes.rb, map.resources :teams to map.resources :teams, :member=>{:new_contest => :get}
Now I get following error: new_contest_team_url failed to generate from {:controller=>"teams", :action=>"new_contest"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: ["teams", :id, "new_contest"] - are they all satisfied?
I don't think adding :member => {...} is the right way doing this. So, can you tell me what to do? I want to have an URL like /teams/new-contest or something.
My next question: what to do (after fixing the first problem), to validate presentence of all fields for new_contest.html.erb? In my normal new.html.erb, a user does not need all the data. But in new_contest.html.erb he does. Is there a way to make a validates_presence_of only for one action (in this case new_contest)?
UPDATE:
Now, I removed my :member part from my routes.rb and wrote:
map.new_contest '/teams/contest/new', :controller => 'teams', :action => 'new_contest'
Now, clicking on my link, it redirects me to /teams/contest/new - like I wanted - but I get another error called:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
I think this error is cause of #team at <% form_for #team, :url => { :action => 'create_content_team' } do |f| %>
What to do for solving this error?
I'm not sure about how your models work, but in my code I've always written;
#team.user_id = #current_user.id
instead of
#team.user_id = current_user.id
That would mean the id wasn't being passed to the controller giving you the error, wouldn't it?
Okay, I found my errors.
For the record:
First of all, I forgot to write the code inside my def new_contest. Here it is:
def new_contest
if #can_create
#team = Team.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #team }
end
else
redirect_back_or_default('/')
end
end
There were several typos, too, in my .erb file like text_fiels instead of text_field or create_content instead of create_contest.
current_user is working fine for me.
I have created a new migration:
class AddSignatureToUser < ActiveRecord::Migration
def self.up
add_column :users, :signature, :text
end
def self.down
remove_column :users, :signature
end
end
Now my usertable has a new column called signature.
On my edit page I wrote:
<h1>Editing user</h1>
<% form_for(#user) do |f| %>
<%= f.error_messages %>
<div class="form-wrapper">
<p>
<label for="email">Email</label>
<%= f.text_field :email %>
</p>
<p>
<label for="user_signature">Signature</label>
<%= f.text_area(:signature, :value => #user.signature) %>
</p>
<div class="form-submit">
<%= f.submit 'Update', :class => "form-submit-button" %>
</div>
</div>
<% end %>
But this won't work.
In my controller I always get nil as value for signature. Any ideas why?
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
puts #user.signature #always nil
flash[:notice] = 'User was successfully updated.'
format.html { redirect_to(#user) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
Check to make sure you ran the migration for the proper environment (development, production)
RAILS_ENV=development rake db:migrate
This is the default, but you may be setting the environment somewhere. I think you'd get a method not found error if you hadn't, but just be sure, I've been hot by this before.
Next, if you're using the mongrel/webrick, try using the debugger, by starting the server with:
./script/server --debugger --environment=development
And then in your controller:
respond_to do |format|
debugger
if #user.update_attributes(params[:user])
puts #user.signature #always nil
...
And check what params is here, specifically params[:user][:signature], make sure it's getting passed correctly.
Lastly, in the view, all you need is:
<%= f.label :signature %>
<%= f.text_area :signature %>
The value will already be the current value since you're calling the form on #user in the form_for. The explicit setting of :value might be interfering somewhere
Two quick questions-
Why are we looking at edit.html.erb and update here? Did you already create this record with new.html.erb and create?
Why do you have <%= f.text_area(:signature, :value => #user.signature) %> instead of just <%= f.text_area :signature %>
Okay, I found my error!
In my user model, I had
attr_accessible :login, :email, :password, :password_confirmation
I added :signature and now it's working!
attr_accessible :login, :email, :password, :password_confirmation, :signature
Just to be sure, you have run rake db:migrate to run the migration, yes?