I have the following polymorphic association set up with acts_as_relation
Model code:
class Email < ActiveRecord::Base
belongs_to :detail
validates_presence_of :address
end
class Detail < ActiveRecord::Base
acts_as_superclass
has_many :emails
accepts_nested_attributes_for :emails, allow_destroy: true
end
class User < ActiveRecord::Base
acts_as :detail
validates_presence_of :username, :password
end
Migration code:
class CreateInfo < ActiveRecord::Migration
def change
create_table :details, :as_relation_superclass => true do |t|
t.timestamps
end
end
end
class CreateEmails < ActiveRecord::Migration
def change
create_table :emails do |t|
t.string :address
t.string :address_type
t.string :detail_id
t.timestamps
end
end
end
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :username
t.string :password
t.timestamps
end
end
end
I'm want to be able to have a form (eventually) that will allow multiple email addresses, addresses and so on. But I'm struggling to get it to work. I use HAML for whoever may reply with the view code, which is a lot more readable.
I have the form currently something like this:
views/users/_form.html.haml
= form_for(#user) do |f|
- if #user.errors.any?
#error_explanation
%h2
= pluralize(#user.errors.count, "error")
prohibited this user from being saved:
%ul
- #user.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :name
= f.text_field :name
.field
= f.label :username
= f.text_field :username
.field
= f.label :password
= f.text_field :password
= f.fields_for :emails do |ff|
.field
= ff.label :address, 'Email address'
= ff.text_field :address
.field
= ff.label :address_type, 'Type'
= ff.text_field :address_type
.actions
= f.submit
If you have a problem with adding nested_attributes, it may be interesting to look on https://github.com/ryanb/nested_form
Related
Firstly this is the first time I have ever tried to use accepts_nested_attributes_for, so I am still learning and hopefully I have just missed something small.
So I have setup and follow an example for doing accepts_nested_attributes_for but I keep getting a ActiveModel::MassAssignmentSecurity::Error at /reports Can't mass-assign protected attributes: deployment
and I can't figure out why, any help would be grateful code is below:
Report Model:
class Report < ActiveRecord::Base
attr_accessible :name, :weekending, :visable, :deployments_attributes
has_many :deployments, dependent: :destroy
accepts_nested_attributes_for :deployments, allow_destroy: true
end
Deployments Model:
class Deployment < ActiveRecord::Base
attr_accessible :deployment_comments, :deployment_name, :environment, :report_id
belongs_to :report
end
report/new.html.erb
<%= form_for (#report) do |f| %>
<%= render 'shared/error_messages' %>
<h4>Reprot Detials</h4>
<%= f.label "Report Written By: " %> <%= f.text_field :name %><br>
<%= f.label "Weekending: " %> <%= f.text_field :weekending %><br>
<%= f.label "Visable: " %> <%= f.text_field :visable %><br>
<%= f.fields_for :deployment do |builder| %>
<%= render 'reports/forms/deployment_fields', f: builder %>
<% end %><br><br>
<%= f.submit class: "btn btn-large btn-primary" %>
<% end %>
report/forms/_deployment_fields.html.erb
<h4>Deployments</h4>
<%= f.label :deployment_name, "Name of Deployment:" %><br />
<%= f.text_field :deployment_name %><br />
<%= f.label :environment, "Environment of Deployment:" %><br />
<%= f.text_field :environment %><br />
<%= f.label :deployment_comments, "Deployment Comments:" %><br />
<%= f.text_area :deployment_comments %><br />
Reports Controller:
class ReportsController < ApplicationController
def index
#reports = Report.paginate(:page => params[:page],
:per_page => 20,
:order => 'Weekending desc')
end
def show
#report = Report.find(params[:id])
end
def new
#report = Report.new
end
def create
#report = Report.new(params[:report])
if #report.save
redirect_to reports_url
else
render 'new'
end
end
DB Migrations:
timestamp_create_deployments.rb
class CreateDeployments < ActiveRecord::Migration
def change
create_table :deployments do |t|
t.string :environment
t.integer :report_id
t.string :deployment_name
t.string :deployment_comments
t.timestamps
end
add_index :deployments, [:report_id]
end
end
timestap_create_reports.rb
class CreateReports < ActiveRecord::Migration
def change
create_table :reports do |t|
t.date :weekending, :null => false
t.string :name, :null => false
t.boolean :visable, :default => false, :null => false
t.timestamps
end
end
end
So figured it out, once I changed
<%= f.fields_for :deployment do |builder| %>
to
<%= f.fields_for :deployments do |builder| %>
I found that the deployments fields didnt show, so then I added
#report.deployments.build
to my new method of my report controller!
I'll just need to figure out if I need to do anything to the update/edit method or not?
I want to print the values taken from two different tables in the database in a table on the view page. I am not getting how to handle two each iterators as it is behaving abnormally i.e Printing a value several times. I am very much confused. Please help.
Here is my code
In the controller:
class ListController < ApplicationController
def all
#books = Book.all
#susers = SUser.all
end
end
In my view page
<tbody>
<% #books.each do |b| %>
<% if b.branch == "I.T" %>
<tr>
<td><%= b.id %></td>
<td><%= b.book_name %></td>
<td><%= b.year %></td>
<td><%= b.user_id %></td>
<% #susers.each do |s| %>
<% if s.user_id == b.user_id %>
<td><%= s.address %></td>
<% else %>
<td>Error..!!</td>
<% end %>
<% end %>
</tr>
<% else %>
<% puts "No any book of this branch" %>
<% end %>
<% end %>
</tbody>
The output is displayed like this
The else part of the first if statement is repeating it self again and again. I dont know why it is happening?
There are 3 models in this project.
1. User - Made by devise
2. Book
3. SUser
One important thing: -
Actually i made SUser model because i want to store user's personal details such as name, address, phone no. I dont want to touch devise model (User) so i made another model SUser which has one to one relation with devise model(User).
User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_and_belongs_to_many :books
has_one :s_user
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
end
Book Model:
class Book < ActiveRecord::Base
# attr_accessible :title, :body
has_and_belongs_to_many :users
belongs_to :s_user, :class_name => "SUser"
attr_accessible :id, :user_id, :book_name, :edition, :author, :branch, :publisher, :year, :details
end
SUser model:
class SUser < ActiveRecord::Base
# attr_accessible :title, :body
has_one :user
has_many :books
attr_accessible :user_id, :fullname, :email, :address, :details
end
Migrations files:
class CreateBooks < ActiveRecord::Migration
def change
create_table :books do |t|
t.integer "user_id", :limit =>5
t.string "book_name", :limit => 50
t.integer "edition", :limit => 5
t.string "author", :limit => 30
t.string "branch", :limit => 30
t.string "publisher", :limit => 50
t.integer "year", :limit => 10
t.text "details"
t.timestamps
end
add_index :books, "user_id"
end
end
SUser migration file
class CreateSUsers < ActiveRecord::Migration
def change
create_table :s_users do |t|
t.integer "user_id", :limit => 5
t.string "fullname", :limit => 25
t.string "email", :default => "", :null => false
t.string "hashed_password", :limit => 40
t.string "salt", :limit => 40
t.string "address",:limit => 25
t.text "details"
t.timestamps
end
add_index :s_users, "user_id"
end
end
I made many to many relationship between user and book since one user have many books and one book can be available to many users.
So i made a simple join table for many to many association
class CreateBooksUsersJoin < ActiveRecord::Migration
def up
create_table :books_users, :id => false do |t|
t.integer "book_id"
t.integer "user_id"
end
add_index :books_users, ["book_id", "user_id"]
end
def down
drop_table :book_users
end
end
Lol.. I have pasted my whole code over here. Actually i am new to rails.. Please guide me if you find any other flaw to this code.
Thanks
class Book < ActiveRecord::Base
belongs_to :suser
end
class SUser
has_many :books
end
class ListController < ApplicationController
def all
#books = Book.includes(:susers).all
end
end
<tbody>
<% #books.each do |b| %>
<% if b.branch == "I.T"%>
<tr>
<td><%= b.id%></td>
<td><%= b.book_name%></td>
<td><%= b.year%></td>
<td><%= b.user_id%></td>
<td><%= b.suser.address %></td>
</tr>
<%else%>
<% puts "Branch has no books"%>
<%end%>
<%end%>
</tbody>
Finally you will need a foreign key for the relationship, something like:
script/generate migration add_user_id_to_books
migration syntax can be tricky so open up the migration file (in db/migrate) and make sure that it is doing something similar to
add_column :books, user_id, integer
You can define relations between your models, I think one to many relation type is suitable for your situation:
class Book < ActiveRecord::Base
belongs_to :suser, :class_name => "SUser"
end
class SUser < ActiveRecord::Base
has_many :books
end
Then in your controller you can write like this:
class ListController < ApplicationController
def all
#books = Book.includes(:suser).all
end
end
And finally your view will look like:
<tbody>
<% #books.each do |b| %>
<% if b.branch == "I.T"%>
<tr>
<td><%= b.id%></td>
<td><%= b.book_name%></td>
<td><%= b.year%></td>
<td><%= b.user_id%></td>
<td><%= b.suser.try(:address) %></td>
</tr>
<%else%>
<% puts "No any book of this branch"%>
<%end%>
<%end%>
</tbody>
P.S.: it's normal that you have repeating of else block because you check for each user if book.suser_id == suser_id (but there is a one to many relation between books and susers, so book belongs to only one user, to few in case you have many to many relation)
I have a business model which has_one address and I am trying to build a form that accepts address attributes when the business is being created. I am getting error
Can't mass-assign protected attributes: address
Here are my models
Business
class Business<ActiveRecord::Base
has_one :address, :as => :addressable
attr_accessible :name, :email, :address_attributes, :password, :password_confirmation
validates_presence_of :address
validates_associated :address
accepts_nested_attributes_for :address
end
Address
class Address<ActiveRecord::Base
attr_accessible :line1, :city, :zip
validates_presence_of :line1, :city, :zip
belongs_to :addressable, polymorphic: true
end
View
%h2 Sign up
= form_for(:business, :url => business_registration_path) do |f|
= devise_error_messages!
%div
= f.label :name
%br/
= f.text_field :name
%div
= f.label :email
%br/
= f.email_field :email
%div
= f.label :password
%br/
= f.password_field :password
%div
= f.label :password_confirmation
%br/
= f.password_field :password_confirmation
%div
=f.fields_for :address do |address|
=render :partial => 'businesses/shared/address', :locals => {:f => address}
%div= f.submit "Sign up"
= render :partial => "devise/shared/links"
Partial view
%div
= f.label :line1, 'Address 1'
%br/
=f.text_field :line1
%div
= f.label :city
%br/
= f.text_field :city
%div
= f.label :zip, 'Postal Code'
%br/
= f.text_field :zip
Raw POST Data THIS IS WHERE I THINK THE PROBLEM IS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"/h17NgMDr4VCTDd+FxGlAI4RWmfAat9guU9q00hYIA4=", "business"=>{"name"=>"hello", "email"=>"hello#gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "address"=>{"line1"=>"this is line1", "city"=>"Andra", "country"=>"Jama", "zip"=>"123123"}}, "commit"=>"Sign up"}
shouldn't the address fields go like this
"address_attributes"=>{"line1"=>"this is line1", "city"=>"Andra", "country"=>"Jama", "zip"=>"123123"}
Why is it generating address instead of address_attributes ? This might be causing the issue. Any ideas? I have been struggling with this for about 2 hours. Appreciated any suggestions or solutions.
Upadte1:
If I make a change in the view and use
=f.fields_for :address_attributes do |address|
instead of
=f.fields_for :address do |address|
Everythings starts working but this isn't what all the tutorials and the docs are talking about ??
You should be passing an object (instance of the Business class) to form_for rather than a symbol:
= form_for(#business, :url => business_registration_path) do |f|
Presumably, you will have #business = Business.new in the controller action.
The Business class contains all the validation and association logic. Use a symbol when you want a modeless form (Rails does not infer that :business refers to the Business class, even if they share the same name).
I am new to Rails 3 and having trouble with saving records in the Join table. I have been looking around and trying different examples found on this website and from the documentation or books, but I don't understand why I can't get it to work. I am trying to create Authorization by creating Roles and associate them to users. So far I have been trying to assign roles from the update action in the Users controller without prevail.
I have 3 models: the User.rb, role.rb, and assignment.rb (the join table)
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password, :role_ids
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :status, :description, :username, :roles_attributes
...
end
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments, :foreign_key => :user_id
accepts_nested_attributes_for :users
attr_accessible :name
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :roles
end
The Users controller in the update action I have the following
class UsersController < ApplicationController
...
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#user.roles.build
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
...
end
and in the 'edit' view page I intend to have checkboxes to update the User record with an associated role:
EDIT: Changed the "check_box" with "check_box_tag" ... the check boxes appear properly, but the values are not saved.
<%= form_for(#user) do |f| %>
...
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[roles][]", role.id, #user.roles.include?(role.id) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
<% end %>
</div>
<% end %>
With this code I even get an error where 'Roles' have no association.
EDIT: this was corrected with the accepts_nested_attributes_for :role. Thanks!
No association found for name `roles'. Has it been defined yet?
I am really confused where I am doing something wrong. Your help would be much appreciated.
Aurelien
You have to use the same name with "accepts_nested_attributes_for" as you used defining the association:
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :role
end
Finally solved the problems and thought I could share.
The models associations but I did change the attr_accessible:
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password
attr_accessible ..., :roles_ids
...
end
In the User controller for the edit and update action.
def edit
#title = "Edit" + " " + #user.username
#roles = Role.find(:all)
#user.assignments.build
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
The important part was the view part and assigning the right names for the checkbox tags
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[role_ids][]", #user.id %>
<% end %>
</div>
The check_box_tag lets the form save an array and gives more control than check_box
Then in order to assign the multiple Role ids, the name of the check_box_tag should include user[roles_ids][].
Finally the last parameter of the check_box_tag returns if the User has already the roles and checks the checkboxes if true.
I must admit that the name part of the check_box_tags is really confusing but it works :).
I am trying (unsuccessfully) to setup Authlogic for my Rails3 project. In the user_sessions#new view, I keep getting the following error:
undefined method `login' for #<UserSession: no credentials provided>
Extracted source (around line #7):
5: <%= form_for(:user_session, :url => user_session_path) do |f| %>
6: <%= f.label :login %><br>
7: <%= f.text_field :login %><br>
8: <%= f.password_field(:password) %>
9: <%= f.submit %>
10: <% end %>
I believe that the error is coming because Authlogic is failing to detect which database column should be used for the login field.
I was under the understanding that Authlogic is supposed to fall back on the :email column if no login column is present. For belt and braces I tried adding a config option to the User class but it still did not solve the problem
Here is my code:
User model
class User < ActiveRecord::Base
attr_accessible :password, :password_confirmation
acts_as_authentic do |c|
c.login_field = :email
end
end
UserSessions controller
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
# ...
end
UserSessions#new view
<%= form_for(:user_session, :url => user_session_path) do |f| %>
<%= f.label :login %><br>
<%= f.text_field :login %><br>
<%= f.password_field(:password) %>
<%= f.submit %>
<% end %>
UserSession model
class UserSession < Authlogic::Session::Base
# configuration here, see documentation for sub modules of Authlogic::Session
end
DB Schema (for authlogic tables)
create_table "sessions", :force => true do |t|
t.string "session_id", :null => false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
create_table "users", :force => true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name"
t.string "last_name"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
end
Why won't Authlogic use the login column I tell it to (or is this even the problem)?
You're telling AuthLogic to use the email field, but then using the login field in your view instead. Change your view to:
<%= form_for(:user_session, :url => user_session_path) do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %><br>
<%= f.password_field(:password) %>
<%= f.submit %>
<% end %>
and it should work.
This error can actually occur for multiple reasons. The one that affects me most often is NOT having the users table created.