I'm trying to build up on the following tutorial from railscast:
http://railscasts.com/episodes/196-nested-model-form-part-1
I'm trying to make everything work with mongodb and mongoid.
the scenario is:
I want to creates events linked to a location. Each events (dance class) contains many lessons.
So I thought that an embedded relationship would be perfect.
Here are my models
model Lesson
class Lesson
include Mongoid::Document
include Mongoid::Slug
field :name, :type => String
embedded_in :event
slug :name
end
model Event
class Event
include Mongoid::Document
include Mongoid::Slug
include Mongoid::Timestamps
include Mongoid::MultiParameterAttributes
field :name, :type => String
field :description, :type => String
field :date, :type => DateTime
validates_presence_of :name
has_one :venue
referenced_in :venue
embeds_many :lessons
slug :name
end
model Venue
class Venue
include Mongoid::Document
include Mongoid::Slug
include Mongoid::Timestamps
include Mongoid::MultiParameterAttributes
field :name, :type => String
field :location, :type => String
validates_presence_of :name, :location
belongs_to :event
slug :name
end
event controller
def create
#event = Event.new(params[:event])
if #event.save
flash[:notice] = 'Event was successfully created.'
end
respond_with(#Event, :location => events_url)
end
def update
# #event = Event.find(params[:id])
#event = Event.find_by_slug(params[:id])
if #event.update_attributes(params[:event])
flash[:notice] = "Event was succesfully updated"
end
respond_with(#event)
end
Then I have my Event view where I can create events and link it to a Venue. But I'd like to be abe to create the lessons from the Event view/model.
so I used the fields_for to generate a field linked to the Lessons model.
= form_for #event do |f|
.field
= f.label :name
%br/
= f.text_field :name
.field
= f.label :description
%br/
= f.text_area :description
.field
= f.label :venue_id
%br/
= f.collection_select :venue_id, Venue.all, :id, :name
.field
= f.label :date
%br/
= f.datetime_select :date
%h3 Add a Class
= f.fields_for :lessons do |builder|
= render "lesson_fields", :f => builder
.actions
= f.submit 'Save'
When I create or edit a new event I get an error message:
undefined method `extract_id' for "test":String
But the request parameter message on the error page shows my lessons value in the Event document.
"lessons"=>{"name"=>"test name lesson"}
When I remove the fields_for line, everything works fine. But then i don't know how to save the value for the nested documents.
I have same problem with embeds_many, but when i try change to has_many. It works!. Maybe you can try too.
can you post the exact code you use to create the Event, including parameters?
which version of Mongoid and Rails are you using?
First thing I noticed is that the following parameter hash does not match your Lessons model:
"lessons"=>{"content"=>"test name lesson"} # this looks wrong
this should be:
"lessons"=>{"name" => "test name lesson"}
Looks like your lessons form has the wrong label for the text input field .. it should be :name , not :content
To dry things up, you might want to try if the 'nested_form' gem works for you:
after installing the gem, use the nested_form_for instead of form_for in your view.
Check here for a more detailed description:
How can I handle this type of multi level forms in rails
See:
https://github.com/ryanb/nested_form (it's also referenced in the RailsCast you mentioned)
You also might want to check this:
field_for and nested form with mongoid
The conclusion of this story is...
I removed everything related to mongoid_slug and it started to work.
I then put everything back as it was to try to find out how to make it work with mongoid_slug and it just worked, like out of the box.
:(
Please include the following code in model event.rb
**accepts_nested_attributes_for :lessons**
This will fix your problem
Related
I came back to the relatively "old book" Head First rails, which was published for Rails 2.3.
Now, going back again through those samples and using Rails 3 I came up with some questions.
Let's say that I'm adapting the sample for coconut airways and instead of flights and seats, I have a project and tasks.
The page shows a project description and below a list of tasks associated to that project. so far so good. now below that there is a form to create new task. This task needs a Task object and the project_id. here is when things do not work as before.
if you want to do it like the old style you will type:
<%= render :partial => "new_task",
:locals => {:task => Task.new(#project.id)} %>
well, this is showing the mass-assign error.
Then I tried to pass both as parameter:
<%= render :partial => "new_task",
:locals => {:task => Task.new, :project_id => #project.id} %>
and assign it in the partial
<%= f.hidden_field :project_id, :value => project_id %>
any hint?
EDITED:
class Task < ActiveRecord::Base
belongs_to :project
attr_accessible :title
end
class Project < ActiveRecord::Base
has_many :tasks
attr_accessible :description, :title
end
If you change your model's attr_accessible you can include these assignments to be made. For more information about attr_accessible and mass assignment see: Ruby on Rails API
I have an account dashboard that lists the offices and for each office the jobs available.
Hierarchy:
Companies (1 user has 1 company, I access if from the user profile)
Offices (each company can have multiple offices)
Jobs (each office can have multiple jobs)
Models:
class Company < ActiveRecord::Base
has_many :offices, :dependent => :destroy
has_many :jobs, :through => :offices
class Office < ActiveRecord::Base
belongs_to :company
has_many :jobs, :dependent => :destroy
class Job < ActiveRecord::Base
belongs_to :office
For each job I have an edit link
E.g. for job id 10 (job is a local variable from iteration on office jobs)
= link_to edit_job_path(I18n.locale, job)
-> localhost:3000/de/jobs/10/edit
When I click on the edit link, I go to the edit page. So far so good, but the form looks like that:
<form accept-charset="UTF-8" action="/10/jobs/10" class="edit_job" enctype="multipart/form-data" id="edit_job_10" method="post">
Notice, that my locale (de in this example) disappeared and has the job id instead!
My routes.rb
scope "/:locale" do
resources :companies
resources :offices do
resources :jobs
end
resources :jobs
end
There are two jobs mentioned, I probably can do without but it's an easy way for me to either mention directly the job url to view or add office variable in the create new job link and use :office_id (in my dashboard controller: link_to new_office_job_path(I18n.locale, office) then in my form for new jobs: = f.hidden_field :office_id)
But even if I remove the resources :jobs in the :offices. The locale is still replaced by the job id in the edit form.
Note that I can edit the job properly, but because the locale is changed, the localisation text are all wrong after my redirect.
Any idea how to fix that?
------ Additional data requested -----------
= form_for(#job) do |f|
.field
= f.label :name, t(:job_title)
= f.text_field :name
.field
= f.label :url, t(:job_url)
= f.text_field :url
.field
= f.hidden_field :office_id
.field
= f.label :pdf, t(:job_upload_pdf)
= f.file_field :pdf
.field
= f.label :tag_list, t(:job_tags)
= f.text_field :tag_list
.actions
= f.submit "Submit", :class => "btn btn-primary"
------ Additional information -----------
BTW: this work around works, I get /de/jobs/10 but I'd like to understand why the locale gets corrupted if I use the default form_for.
= form_for #job, :url => job_path(I18n.locale, #job) do |f|
You can handle the locale using a Routing Filter, I've tried it in my project, you don't have to worry about locale in routes, the filter will handle it for you.
# in config/routes.rb
Rails.application.routes.draw do
filter :locale
end
I hope this will solve your problem.
The form_for line builds the path, when building the path you also need to specify the locale. To do this cleanly (without specifying the url explicitly, which is also possible), write it as follows:
= form_for [I18n.locale, #job] do |f|
and that should render the correct path.
As specified in the documentation it will use the array to build the correct path (this works for namespaced and nested routes, so I am guessing it will also work for your locale).
An alternative is to specify the path explicitly, using the :url option.
HTH.
have some custom attr_accessor in my model.
When I try to create a new model using the params[:model] I'm getting this error:
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: entity_select, office_select):
class Expedient < ActiveRecord::Base
belongs_to :enterprise
has_many :document
attr_accessor :entity_select
attr_accessor :office_select
...
I suppose Rails know that those are att_accessor and should not be saved on the model in the database.
Or not ?
I'm using this attr_accessor for creating some help field using simple_form:
<%= f.input :entity_select,:label => 'Entity', :input_html => {:class => "span2"}, :wrapper => :prepend do %>
<span class="add-on"><i class="icon-search"></i></span><%= f.input_field :entity_select, :class => "span2 typeahead_entity", :id_selected => '99', :id => 'typeahead_centre'%>
<%end%>
I need a special text input for a typeahed, but I want to create this input using simple_form, like other 'real' fields, that's why I'm using a 'not real attribute'...
thanks,
EDIT:
The error is not when saving, just before, when assigning the params to the model attributes here:
#expedient=Expedient.new.attributes=params[:expedient]
Make the entity_select and office_select attributes accessible:
class Expedient < ActiveRecord::Base
# Add:
attr_accessible :entity_select, :office_select
I want to add multiple emails to the user model while using devise, using a has_many relationship. It's a bit of an extension to this question: How can I configure Devise for Ruby on Rails to store the emails and passwords somewhere other than in the user model?
User.rb
has_many :emails
Email.rb
belongs_to :user
In my new/edit form for devise how do I set up the fields_for xxx.email. I can see that the registration page from devise uses "resource". When I try the following code it just skips the email field.
= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
= devise_error_messages!
%table{:style=>"width:400px;"}
-fields_for resource.email do |emails|
%tr
%td
= emails.label :email
%td
= emails.email_field :email
%tr
%td
= f.label :password ...
%tr
%td
= f.label :firstname ....
Also, who do you modify the actions in the controller for devise?
In this case, the entire email block does not apear.
Thanks
The helper fields_for need some data to create the fields. With new records, usually is needed you do something like resource.emails.build to create a first record to be showed.
And also, I think you should use fields_for(resource.emails) do |email|.
I have pulled out all my hair. No more left... :(
I am using Spree 0.3.4, within an extension I need to register some retailers up. so I direct them to a retailers form which has many custom fields which belong to a retailer model...
So I am trying to validate/submit all the fields from one form like so
myextension/app/views/user_registrations/new.html.erb
<%= form_for (:user, :url => registration_path(#user, :type => "retailer) do |f| %>
<%= f.fields_for :retailer do |r| %>
<%= r.text_field :name %>
<% end %>
<%= f.text_field :email %>
<% end %>
etc etc
class Retailer < ActiveRecord::Base
belongs_to :user
validates :name,
:presence => true
end
class User < ActiveRecord::Base
has_one :retailer
accepts_nested_attributes_for :retailer
attr_accessible :retailer_attributes
# theres a whole lot more spree and devise stuff here. not sure worth mentioning
end
I have also added the abilities in the cancan ability.rb
The problem is the retailer feilds never get validated and the data is never inserted into the database...
I created a blank app, and tried this process from scratch with some plain old scaffolding and it works fine.
any ideas??
In your application helper, do something like this(assuming your have Ruby 1.9.* for the tap functionality, otherwise checkout rails returning here):
def setup_user(user)
user.tap do |u|
u.build_retailer if u.retailer.nil?
end
end
then in your view change it to this:
<%= form_for (setup_user(#user), :url => registration_path(#user, :type => "retailer) do |f| %>
See if that works.