I have a User Model and an Instructor Model. There is a one-to-one relationship between user and instructor. And some users will be instructors and some will not. As such I have a registration form that uses a fields_for method to write to both.
How can I write to the instructor table only on the condition that they say they are an instructor, such as through a checkbox. And when they do write I want to maintain my validations of the table along with the rest of the form
Ideally this would work best if I can do this through the model, but I'm open to all suggestions.
Instructor Model
class Instructor < ActiveRecord::Base
belongs_to :user
validates_presence_of :school_url, :etc...
attr_accessible :school_url, :etc...
end
User Model
class User < ActiveRecord::Base
has_one :instructor, :dependent => :destroy
validates_uniqueness_of :email
validates :email, :confirmation => true
accepts_nested_attributes_for :instructor
attr_accessible :email, :password, :instructor_attributes, :etc
end
Form in HAML
- resource.build_instructor
- form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
= hidden_field_tag :destination, { :value => destination}
.field
= f.label :firstname, "First Name"
= f.text_field :firstname
.field
= f.label :lastname, "Last Name"
= f.text_field :lastname
.field
= f.label :email, "E-Mail"
= f.email_field :email
.field
= f.label :email_confirmation, "Confirm E-Mail"
= f.email_field :email_confirmation
.field
= f.label :password
= f.password_field :password
.field
= f.label :password_confirmation, "Confirm Password"
= f.password_field :password_confirmation
#instructor-box
%p
%span.bold Are you an instructor?
= check_box_tag :instructor_check
%span Yes, I am an instructor
= f.fields_for :instructor do |i|
= render "/users/registrations/instructor", :form => i
I fixed the problem. It seems all too obvious now. In order for the fields_for to be cancelled out all I have to do is delete the instructor_attributes that are being created by the form in the controller. For example:
Create
# params[:user] => {:email => "justin#example.edu", ..., :instructor_attributes => { :school_url => "www.example.edu", ...}
# params[:instructor_check] => "0"
Given these parameters being passed, I can easily delete the attributes that are to be saved and the rails no longer tries to create a new record for instructor that's to be associated with user. This is literally the code I used. Not the most elegant, but it works.
params[:user].delete :instructor_attributes if params[:instructor_check] = "0"
This recognizes that no instructor profile is being created for the user and thus does not write to the table. Before it was sending back blank attributes and failing on the validations.
Related
I have 2 types of Users: Normal users and pros.
Pros are Users, but have extra fields in a separate table called :pros.
So , I did a separate registration form for :pros, in which I included :users fields, and added a fields_for with the new :pro fields.
I also added those new parameters to application_controller, so that devise permits them.
When submiting the registration form, the user is created, but I get the following error in my logs:
Started POST "/users" for 127.0.0.1 at 2014-11-13 00:53:43 +0100
Processing by RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zUVLJFHhShoHvUVneGNmCf46E4KPWaINeTw4o7iCa7w=", "user"=>{"name"=>"asdasd", "email"=>"asdasd#sss.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "pros"=>{"type"=>"Marca de Decoración", "web"=>"asadasd", "telephone"=>"765876", "about"=>"sadasd"}, "tos_agreement"=>"1"}, "commit"=>"Registrarme y aplicar para PRO"}
Unpermitted parameters: pros
My view is:
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= f.label :name, "Nombre de usuario" %>
<%= f.text_field :name, :autofocus => true %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation %>
<%= f.fields_for :pro do |pro| %>
<%= pro.select :type,["Marca de Decoración","Tienda de Decoración","Blogger"] %>
<%= pro.text_field :web, placeholder: "http://www.miweb.com" %>
<%= f.label :telephone, "Teléfono" %>
<%= pro.text_field :telephone, placeholder: "Teléfono", label: "Teléfono de contacto" %>
<%= pro.text_field :about%>
<% end %>
Users Controller new action
def pro_new
render "devise/registrations/new-pro-registration"
#user = User.create
end
My model relations:
User.rb
has_one :pro
accepts_nested_attributes_for :pro, allow_destroy: true
Pro.rb
belongs_to :user
My application controller:
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :tos_agreement, :avatar, :avatar_cache, :email, :password, :password_confirmation, pros_attributes: [:pro_name, :pro_image, :is_active, :web, :user_id, :about, :facebook, :twitter, :linkedin, :telephone]) }
end
I completely agree with #smallbutton.com
You need to change pro_attributes instead of pros_attributes. you can use params.require(:user).permit! if you want to accept all the params.
You have association like user has_one :pro.
Then you have passed array in permitted parameters but it is has_one relation.
It should be like this.
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :tos_agreement, :avatar, :avatar_cache, :email, :password, :password_confirmation, pro_attributes: (:pro_name, :pro_image, :is_active, :web, :user_id, :about, :facebook, :twitter, :linkedin, :telephone)) }
end
It will works :)
Try to change your controller to this so that a new instance of pro is build and your form helper should produce pro_attributes. I assume this is the new action so you dont call #create but #new on your user. Maybe you need to change pros_attributes to pro_attributes in the permitted_parameters method because its a has_one relation.
def pro_new
#user = User.new
#user.build_pro
render "devise/registrations/new-pro-registration"
end
Hope this will do the trick.
I could give you a quick answer, but I think your design needs some work. You'd be far better of (especially in the long run) with the following format using single table inheritance and delegations
User #(base class)
has_one :userInfo
delegate :list_of_pro_fields, to: :userInfo
BasicUser < User #(sub class)
Pro < User #(sub class)
validates presence: true, [:list_of_pro_fields]
UserInfo #(base class)
belongs_to :user
While you could put the relationship between Pro and ProInfo I'd keep it on the base class if a user is able to lose (and regain) their pro status. This will prevent orphan records in UserInfo of ex-Pros.
This is also going to result in far better data segregation and response times as many fields you wont need a lot of the time are being moved to a separate table which can be joined as required.
So I'm working on a project where I have a document object (basically an E-Library application) and I have a bunch of Tag objects that I want to be able to associate with it. Currently I have a has_and_belongs_to_many association between the two. My question is in the form for a tag, what is the best way to select from a list of available tags to associate with that document? And will I have to do any fancy work in the controller to make this happen?
I'm using rails 3.2
Here is some of the code:
# This is the text model
# It will not have an attachment but instead it's children will
class Text < ActiveRecord::Base
attr_accessible :name, :author, :date, :text_langs_attributes, :notes
has_many :text_langs, dependent: :destroy
belongs_to :item
validates :author, presence: true
has_and_belongs_to_many :tags
accepts_nested_attributes_for :text_langs
def get_translations
TextLang.where(:text_id => self.id)
end
def get_language(lang)
TextLang.where(:text_id => self.id, :lang => lang).first
end
end
This is the tag:
# This is the Tags class
# It has and belongs to all of the other file classes
# the tags will need to be translated into four langauges
# Tags will also own themselvea
class Tag < ActiveRecord::Base
attr_accessible :creole, :english, :french, :spanish, :cat,
:english_description, :french_description, :spanish_description,
:creole_description, :parent_id
has_and_belongs_to_many :texts
has_and_belongs_to_many :sounds
belongs_to :parent, :class_name => 'Tag'
has_many :children, :class_name => 'Tag', :foreign_key => 'parent_id'
validates :cat, presence: true, inclusion: { in: %w(main sub misc),
message: "%{value} is not a valid type of tag" }
validates :english, :spanish, :french, :creole, presence: true
TYPES = ["main", "sub", "misc"]
end
This is the form:
= form_for #text do |f|
- if #text.errors.any?
#error_explanation
%h2= "#{pluralize(#text.errors.count, "error")} prohibited this text from being saved:"
%ul
- #text.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :date
= f.date_select :date
.field
= f.label :author
= f.text_field :author
= f.fields_for :text_langs do |pl|
.field
= pl.label :title
= pl.text_field :title
.field
= pl.label :lang
= pl.text_field :lang
.field
= pl.label :description
= pl.text_field :description, :size => 150
.field
= pl.label :plain_text
= pl.text_area :plain_text
.field
= pl.label :published
= pl.check_box :published
.field
= f.label :txt
= f.file_field :txt
.field
= f.label :notes
= f.text_area :notes, :rows => 10
.actions
= f.submit 'Save'
First of all I would suggest to try simple_form gem it would make your forms DRY and simple. They have very nice features for associations.
You would end doing something like this:
= simple_form_for #text do |f|
...
= f.association :tags, as: :check_boxes
Could be check boxes, radio buttons or maybe a select with multiple values if you need it.
Hope it helps
I have a rails 3.1 app with devise:
User has_one profile
Profile belongs_to user
Overruled the devise registration_controller
Custom registration views all working fine, registration works fine
Now I could like to add this:
On the registration page I want to add fields from the profile, like first name, lastname
There is no user yet, that is going to be created when submitting the form
I need the profile to be created with this first name,last
How would I do this? I tried several ideas, also from stack overflow but cannot seem to get it working. I tried nested attributes wich is not working the way to do this would to create a profile record in the db at the moment the user registers wich inserts the first name and last name
My registrations#new view:
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
= devise_error_messages!
= f.input :username, :label => 'Username'
= f.input :email, :label => 'Email'
= f.input :password, :label => 'Password'
= f.input :password_confirmation, :label => 'Password confirm'
// start fields for profile
= f.fields_for :profile do |f|
= f.label :bod_day
= f.text_field :bod_day
// end fields for profile
= f.button :submit, t(:submit, :scope => :register)
In my user model i have this:
accepts_nested_attributes_for :profile
I think the problem is that no profile exists on that user when the form is rendered, a simple way to get round this might be to build it in memory before rendering the fields like so:
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
= devise_error_messages!
= f.input :username, :label => 'Username'
= f.input :email, :label => 'Email'
= f.input :password, :label => 'Password'
= f.input :password_confirmation, :label => 'Password confirm'
// make a new profile in memory
= resource.build_profile
// start fields for profile
= f.fields_for :profile do |f|
= f.label :bod_day
= f.text_field :bod_day
// end fields for profile
= f.button :submit, t(:submit, :scope => :register)
In addition to Nesbitt's solution, add this to your model
attr_accessible ..., :profile_attributes
I am working with polymorphic associations and having some trouble. My models are setup like so:
class User < ActiveRecord::Base
has_one :phone, :as => :callable, :dependent => :destroy
end
class Client < ActiveRecord::Base
has_one :phone, :as => :callable, :dependent => :destroy
end
class Phone < ActiveRecord::Base
belongs_to :callable, :polymorphic => true
end
In my Users Controller
def create
#user = User.new(params[:user])
if #user.save
#user.phone.create(:area_code => params[:user][:area_code], :phone => params[:user][:phone])
redirect_to #user, :notice => "Account created successfully!"
else
render 'new'
end
end
In the development log I see where the phone and user are being inserted correctly, but when I go to edit the user, the fields for phone in the form are blank. Here is my edit method:
def edit_employee
#user = User.find(params[:id])
#title = "Edit #{#user.name}"
end
My edit user form looks like this.
- form_for #user do |f|
- if #user.errors.any?
.error_messages
%h2 Please correct the following errors
%ul
- for message in #user.errors.full_messages
%li= message
%p
= f.label :name, "Name"
= f.text_field :name
%p
= f.label :email, "Email Address"
= f.text_field :email
%p
= f.label :phone, "Phone"
= f.text_field :area_code, :style => "width: 50px;"
= f.text_field :phone, :style => "width: 100px;"
= f.label :ext, "Ext."
= f.text_field :extension, :style => "width: 60px;"
%p
= f.label :password, "Password"
= f.password_field :password
%p
= f.label :password_confirmation, "Confirm Password"
= f.password_field :password_confirmation
%p.button= f.submit
I know I should be adding something to this edit method, perhaps
#phone = #user.phone
But that didn't work either. This is the first go round with polymorphic associations so any help and and pointers are much appreciated. I watched the Railscasts on this topic but it didn't seem to follow my underlying functionality. Once again, thanks in advance for any help and let me know if any more information is needed!
you should look into using fields_for and nested_attributes. http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Do you have attr_accessible set in the user.rb model? If not I would add it because that is a security issue.
Ok I added the
accepts_nested_attributes_for :phone
in the user model. I also added the fields for phone in the new user form like so
%p
- fields_for #user.phone do |phone|
= phone.label :phone, "Phone"
= phone.text_field :area_code, :style => "width: 50px;"
= phone.text_field :phone, :style => "width: 100px;"
= phone.label :ext, "Ext."
= phone.text_field :extension, :style => "width: 60px;"
but now I am getting the ActionView::Template::Error (undefined method `model_name' for NilClass:Class) exception.
And yes I have attr_accessible in my model. I just placed a very watered down version in here.
I am using accepts_nested_attributes_for with the has_one polymorphic model in rails 2.3.5
Following are the models and its associations:
class Address < ActiveRecord::Base
attr_accessible :city, :address1, :address2
belongs_to :addressable, :polymorphic => true
validates_presence_of :address1, :address2, :city
end
class Vendor < ActiveRecord::Base
attr_accessible :name, :address_attributes
has_one :address, :as => :addressable, :dependent => :destroy
accepts_nested_attributes_for :address
end
This is the view:
- form_for #vendor do |f|
= f.error_messages
%p
= f.label :name
%br
= f.text_field :name
- f.fields_for :address_attributes do |address|
= render "shared/address_fields", :f => address
%p
= f.submit "Create"
This is the partial shared/address_fields.html.haml
%p
= f.label :city
%br= f.text_field :city
%span City/Town name like Dharan, Butwal, Kathmandu, ..
%p
= f.label :address1
%br= f.text_field :address1
%span City Street name like Lazimpat, New Road, ..
%p
= f.label :address2
%br= f.text_field :address2
%span Tole, Marg, Chowk name like Pokhrel Tole, Shanti Marg, Pako, ..
And this is the controller:
class VendorsController < ApplicationController
def new
#vendor = Vendor.new
end
def create
#vendor = Vendor.new(params[:vendor])
if #vendor.save
flash[:notice] = "Vendor created successfully!"
redirect_to #vendor
else
render :action => 'new'
end
end
end
The problem is when I fill in all the fileds, the record gets save on both tables as expected.
But when I just the name and city or address1 filed, the validation works, error message shown, but the value I put in the city or address1, is not persisted or not displayed inside the address form fields?
This is the same case with edit action too.
Though the record is saved, the address doesn't show up on the edit form. Only the name of the Client model is shown.
Actually, when I look at the log, the address model SQL is not queried even at all.
Why f.fields_for :address_attributes?
Shouldn't it be:
- f.fields_for :address do |address_fields|
= render "shared/address_fields", :f => address_fields
It's not loading the values on edit and errors because you never load address_attributes with the values from #vendor.address.