When an incorrect mandatory fields, the page reloads and you receive an incorrect entry fields and all fields in the form company_form duplicated.
User model(user.rb):
class User < ActiveRecord::Base
....
has_many :companies, :autosave => true
accepts_nested_attributes_for :companies
has_and_belongs_to_many :roles
def role?(role_name)
return !!self.roles.find_by_name(role_name)
end
def with_company
self.companies.build
self
end
end
Company model:
class Company < ActiveRecord::Base
...
belongs_to :user
...
end
views/devise/registration/new.html.haml:
= form_for(resource.with_company, :as => resource_name, :url => registration_path(resource_name), :html => { :class => 'form-horizontal'}) do |f|
...
= f.fields_for :companies do |company_form|
...
...
The problem is that your with_company method uses build to create the new Company object. build will automatically create the new object and save it to the database. So every time that form gets rendered, you'll add another Company to that User. Just hit reload on the page a few times and you should see what I mean.
I think the fix will be to use create instead of build in that method; this creates a new object but doesn't save it to the database.
More likely, though, you should be doing this in the controller that renders the view, rather than in the view itself. Devise doesn't always make this straightforward, but you should be able to create a new controller that inherits from the Devise controller and adds what you need. I do something similar in my current project here.
worked! change def with_company on
def with_company
if self.companies.empty?
self.companies.build
end
self
end
Related
In my Rails app, I have Steps and Questions. A user can generate a question for any step.
Step.rb
class Step < ActiveRecord::Base
has_one :question, :dependent => :destroy
accepts_nested_attributes_for :question, :allow_destroy => :true
end
Question.rb
class Question < ActiveRecord::Base
belongs_to :step
end
I used a nested form to generate a question:
<%= semantic_form_for [#project,#step] do |f| %>
<%= f.fields_for :question do |question_f| %>
<%= question_f.text_area :description %>
<% end %>
<% end %>
First, I only want to create a new question if the user actually enters text into the question text area. How can I prevent the step form from automatically saving an empty question? Here is my current step controller:
class StepsController < ApplicationController
def new
#step = #project.steps.build(:parent_id=> params[:parent_id])
#step.build_question
...
end
def create
#step = #project.steps.build(params[:step])
respond_to do |format|
if #step.save
...
end
end
Second, I want to run some ruby code when a new question is created. In particular, I want to update the updated_at date for the project that contains the question. Where would I put this in my controller? I tried creating a controller for the Question model and creating create and new methods, but they weren't called when the step form was submitted.
Your first issue can be solved by modifying the nested attributes line in your Step class to reject a submitted question if its attributes are all blank, as follows. See the accepts_nested_attributes_for documentation under :reject_if.
accepts_nested_attributes_for :question, :reject_if => :all_blank, :allow_destroy => true
Your second issue could be solved in a number of ways. If you always want to update the updated_at for a project (also called "touching" it) whenever a question is added to it, you could add a callback to your Question class that touches the project. See this guide on callbacks.
class Question < ActiveRecord::Base
belongs_to :step
has_one :project, :through => :step
after_create :touch_project
private
def touch_project
project.touch
end
end
On the other hand, if you only want to touch a question's project in the context of a specific controller action, you can just check whether a question was created and touch the project if so:
def create
#step = #project.steps.build(params[:step])
if #step.save
if #step.question.present?
#project.touch
end
# do other stuff for successful save
else
# handle failed save
end
end
Also, for future reference, if you have two largely unrelated questions it's better to ask them separately. Combining questions makes it more difficult for others with the same problem to find an answer.
I have a new form that creates an Item (all the codes are obviously simplified):
<%= simple_form_for #item do |f| %>
<%= f.input :brand_name %>
<%= f.button :submit %>
<% end %>
The current user will create an item and link it to a new or to an existing brand.
This field doesn't exist in the database; it'll be used as a way to associate all models. Hence, I create its getter and setter.
def Item < ActiveRecord::Base
belongs_to :user
belongs_to :brand
attr_accessible :brand_name
def brand_name
brand.try :name
end
def brand_name=(name)
if name.present?
brand = user.brands.find_or_initialize_by_name(name)
brand if brand.save
end
end
end
class ItemsController < ApplicationController
def new
#item = current_user.items.build
end
def create
#item = current_user.items.build(params[:item])
if #item.save
...
end
end
end
The problem is that when the form is submitted, I get this error, which lies in the product_name=() method. I've done some debugging through Rails' console and it goes all fine, but in the browser the setter method is called before the create action. That is, the record doesn't even have a user associated to it. I tried leaving the create method empty, for example, but nothing different happens.
undefined method `brands' for nil:NilClass
What is really weird is that this was working a couple of weeks ago (I've checked my git commits and the code is identical).
I though about calling the before_create callback, but there's no way to know which user should be linked.
UPDATE
I'm using Sorcery as the authentication handler. Everything always works fine, except for this create action.
class User < ActiveRecord::Base
authenticates_with_sorcery!
belongs_to :company
has_many :items
end
class Company < ActiveRecord::Base
has_many :users, dependent: :destroy
has_many :brands, dependent: :destroy
end
I am trying to create an app that allows users to create and apply for jobs but seem to have hit a problem.
I can't get the job_id to pass into my apps (job applications) table in my database.
To get this app to work succesfully I need to pass the job_id and the user_id to the user's application form so that when they submit their job application this information is stored in my apps table. The job owner will then be able to review the applications they have received.
I have the following associations in my models:
class App < ActiveRecord::Base
belongs_to :job
belongs_to :user
class Job < ActiveRecord::Base
belongs_to :user
has_many :apps
has_many :applicants, :through => :apps, :source => :user
class User < ActiveRecord::Base
has_many :apps
has_many :jobs
has_many :jobs_applied_for, :through => :apps, :source => :job
Defined on my Jobs controller's show page (the page from which the user can click "apply now" to start an application) I have the following:
def show
#job = Job.find(params[:id])
end
The link to "apply now" on the actual page is:
<%=link_to "Apply Now", new_app_path %>
and on my Apps controller's new page I have:
def new
#user = current_user
#app = #user.apps.build
end
My user_id is passing perfectly and appearing in my apps table but I am totally stumped on how to pass the job_id correctly.
If I have missed anything that I can edit into this question to help you answer it then please do let me know.
Thanks in advance for your help!
You are not passing the job_id in your new_app_path link. Try changing it to new_app_path(:job_id => #job.id), and in your controller add #job = Job.find(params[:job_id])
Assuming your routes nest apps inside jobs, your link to the new application page should be something like
link_to 'apply now', new_app_job_path(#job)
In your new action you'll have a params[:job_id] that you can use to set #job
Then on the form, your call to form for should look like
form_for [#job, #app] do |f|
...
This ensures that your create action will also have a :job_id parameter that you can use when creating the application
The action that you should be invoking is create not new.
Change the link_to code as follows:
<%=link_to "Apply Now", apps_path(:app => {:job_id => #job.id}), :method => :post %>
In your controller:
def create
app = current_user.apps.build(params[:app])
if app.save
# handle success
else
# handle error
end
end
Given the following models:
class Company
include Mongoid::Document
has_many :workers, autosave: true
accepts_nested_attributes_for :workers
attr_accessible :workers_attributes
end
class Worker
include Mongoid::Document
field :hours
attr_accessible :hours
belongs_to :company
end
class Manager < Worker
field :order
has_many :contributors, :class_name => "Worker"
attr_accessible :order, :contributors
end
class Contributor < Worker
field :task
belongs_to :manager, :class_name => "Worker"
attr_accessible :task
end
How does one create a manager in a company in the controller and view using nested attributes?
Here's my guess:
def new
#company = Company.new
#company.workers = [Manager.new]
end
def create
#company = Company.new params[:user]
if #company.save
redirect_to root_url, :notice => "Company with manager created."
else
render :new
end
end
= semantic_form_for #company do |f|
= f.semantic_fields_for :workers do |worker_fields|
= worker_fields.inputs do
= worker_fields.input :hours
= worker_fields.input :order
problem is the order field which specifically belongs to the manager is not persisting after the create. Also when the data is improperly filled there is an error:
undefined method `order' for #<Worker:0x0000000646f018> (ActionView::Template::Error)
So is there a way for nested attributes to handle inheritance in the models from mongoid?
The question is related to Can nested attributes be used in combination with inheritance? except instead of active record using mongoid.
Honestly, this is a paraphrasing of my code... the real code is more complex situation although i believe these are all of the relevant parts. If you have more questions ask.
UPDATE:
I changed the view to the following:
= semantic_form_for #company do |f|
- #company.workers.each do |worker|
- if worker._type == "Manager"
= f.semantic_fields_for :workers, worker do |worker_fields|
= worker_fields.inputs do
= worker_fields.input :hours
= worker_fields.input :order
I do not get the error anymore, however the nested attributes do not update the company object properly. The params are the following:
{"company"=> {"workers_attributes"=>{"0"=>{"hours"=>"30", "order" => "fish", "id"=>"4e8aa6851d41c87a63000060"}}}}
Again edited for brevity. So the key part is that there is a hash between "0" => {data for manager}. The workers data seems to be held in a hash. I would expect the data to look more like the following:
params = { company => {
workers_attributes => [
{ hours => "30", "order" => "fish" }
]}}
This is different because the workers data is held in an array instead of a hash. Is there another step to get the nested attributes to save properly?
Thanks
what version of Mongoid are you using? Because I don't think the use of refereneces_many is encouraged -- Not that that's related to your problem here, just wanted to probe what version you're using. In the doc on the gorgeous Mongoid.org, get this, I had to learn it the hard way, they say for Updating your records, you need to the autossave set to true. That's NOT accurate. You need it for even creating
so:
class Company
include Mongoid::Document
has_many :workers, :autossave => true # your money shot
accepts_nested_attributes_for :workers
attr_accessible :workers_attributes
end
ADDED:
I was re-reading your code, I spotted the following that might be the problem: Your Company model is set to has_many :workers and is set to accept nested attribbutes for Worker when changes come in, correct? And there is a field named Order in your Manager model which is subclassed from Worker. Yet you're having a form whose nested fields part is pointed at Worker not at Manager, the model that actually has the Order field. And that's obviously not enough, because Company isn't having_many :managers yet, you may need to set it to has_many :managers in the Company model as well.
I have a couple classes that can each have comments:
class Movie < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Actor < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
How do I create a form for a new movie-comment? I added
resources :movies do
resources :comments
end
to my routes.rb, and tried new_movie_comment_path(#movie), but this gives me a form containing commentable_id and commentable_type [which I want to be populated automatically, not entered by the user directly]. I also tried creating the form myself:
form_for [#movie, Comment.new] do |f|
f.text_field :text
f.submit
end
(where "text" is a field in the Comment table)
but this doesn't work either.
I'm not actually sure how to associate a comment with a movie at all. For example,
c = Comment.create(:text => "This is a comment.", :commentable_id => 1, :commentable_type => "movie")
doesn't seem to create a comment associated to the movie with id 1. (Movie.find(1).comments returns an empty array.)
As you have created the polymorphic association in your model, you need not worry about that anymore in the view. You just need to do this in your Comments controller.
#movie = Movie.find(id) # Find the movie with which you want to associate the comment
#comment = #movie.comments.create(:text => "This is a comment") # you can also use build
# instead of create like #comment = #movie.comments.create(:text => "This is a comment")
# and then #comment.save
# The above line will build your new comment through the movie which you will be having in
# #movie.
# Also this line will automatically save fill the commentable_id as the id of movie and
# the commentable_type as Movie.
You're going to have to be more descriptive than "...but this doesn't work either," but the general idea is:
#movie.comments.create( :text => params[:movie][:comment][:text] )
More typically:
#movie.comments.create( params[:comment] ) # or params[:movie][:comment]
The important thing is that you find #movie first and create your associated objects through it. That way you won't have to worry about Commentable or types or anything.