How to handle each iterator? - ruby-on-rails-3

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)

Related

has_many join display issue

Newbie Rails question... I know there's a better way to do this. I need help understanding why this isn't working like I thought it should.
I'm doing a simple join tables using a "has_many" relationship with a pre-existing database. I need to keep the "non-rails" friendly titles.
Here's the output <%= room.levels %> as seen in the browser: [#<Level Name: "01 - FIRST FLOOR">]
I'd like to only see 01 - FIRST FLOOR without all of the other information.
I have two tables. :rooms and :levels
Here's the schema for the two tables:
create_table "levels", :primary_key => "Id", :force => true do |t|
t.integer "TypeId"
t.integer "DesignOption"
t.string "Name"
t.float "Elevation"
create_table "rooms", :primary_key => "Id", :force => true do |t|
t.integer "DesignOption"
t.integer "PhaseId"
t.string "Comments"
t.float "Volume"
t.float "Perimeter"
t.integer "Level"
t.string "Occupancy"
t.float "Area"
t.string "Number"
t.string "Name"
end
add_index "rooms", ["Id"], :name => "Id", :unique => true
Here's the app/model/room.rb:
class Room < ActiveRecord::Base
attr_accessible :Area, :Level, :Name, :Number, :Perimeter, :PhaseId, :Elevation
has_many :levels, :primary_key => 'Level', :foreign_key => 'Id', :select => 'Name' set_primary_key :Id
end
Here's a snippet from the app/views/rooms/index.html.erb:
<% #rooms.each do |room| %>
<tr>
<td><%= room.Name %></td>
<td><%= room.Number %></td>
<td><%= room.PhaseId %></td>
<td><%= room.levels %></td>
<td><%= link_to 'Show', room %></td>
<td><%= link_to 'Edit', edit_room_path(room) %></td>
<td><%= link_to 'Destroy', room, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
Thanks!
You can do this:
<td><%= room.levels.map(&:Name).join(', ') %></td>
Why your code didn't work on the first place? because room.levels returns an array of Level objects. You need to loop through them to get each name, and then display it.
room.levels
# => returns all the level objects associated
room.levels.map(&:Name)
# => collect each name of the level objects (makes an array of (String) names)
room.levels.map(&:Name).join(', ')
# => Return a nice String with all the levels name with ", " between each.
because levels gives you a collection back. you need to iterate over room.levels and there you get the elements
With you current Schema, Room belongs_to level, there is no way a room can have many levels (unless not with only that two tables)
create_table "rooms", :primary_key => "Id", :force => true do |t|
...
t.integer "Level" # One-to-One relation
So, what you need is something like:
# Model
class Room < ActiveRecord::Base
attr_accessible :Area, :Level, :Name, :Number, :Perimeter, :PhaseId, :Elevation
belongs_to :level, :primary_key => 'Level', :foreign_key => 'Id', :select => 'Name'
end
# View
<td><%= room.level.name %></td>

Mass assignment error in nested form in Rails

transaction.rb model:
class Transaction < ActiveRecord::Base
attr_accessible :customer, :tickets_attributes
has_many :tickets
accepts_nested_attributes_for :tickets
end
ticket.rb model:
class Ticket < ActiveRecord::Base
attr_accessible :booking_id, :quantity, :transaction_id
belongs_to :transaction
belongs_to :booking
end
in the view page i have a nested rails form for multiple entries of ticket:
<%= form_for(#transaction) do |f| %>
<%= f.text_field :customer %>
<% #sezzion.bookings.each do |booking| %>
<%= booking.bookingdate %>:
<%= f.fields_for :ticket do |t| %>
<%= t.text_field :quantity, :value => 0, :class => "quantity" %>
<%= t.hidden_field :booking_id, :value => booking.id %>
<% end %>
<% end %>
<%= f.submit "create transaction" %>
<% end %>
When I'm submitting the form, I have the following error:
ActiveModel::MassAssignmentSecurity::Error in TransactionsController#create
Can't mass-assign protected attributes: ticket
I have attr_accessible :tickets_attributes and accepts_nested_attributes_for :tickets in the transaction model and there's still an error. Also when I add plural to the ticket on line <%= f.fields_for :ticket do |t| %>, the quantity field doesn't display.
Your form f is based off of a Transaction object, which has_many :tickets. I believe you should be using the plural :tickets rather than the singular :ticket in your fields_for.
<%= f.fields_for :tickets do |t| %>
If you always want a new ticket, you may need to do:
<%= f.fields_for :tickets, Ticket.new do |t| %>
to ensure that a create form shows up.
total re-edit -- sorry its been a while I had to refresh my memory
transaction.rb tickets_attributes is ok.
class Transaction < ActiveRecord::Base
attr_accessible :customer, :tickets_attributes
has_many :tickets
accepts_nested_attributes_for :tickets
end
transaction_controller.rb you must build the tickets.
def new
#transaction = Transaction.new
#transaction.tickets.build
end
new.rb or in your form, fields_for must be for :tickets as rob as pointed out:
<%= form_for(#transaction) do |f| %>
...
<%= f.fields_for :tickets do |t| %>
...
I think you might be missing the build part in the controller. hope that helps!

Polymorphism and nested attributes

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

saving record to join table Has_many :through

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 :).

Show Name instead of ID in a HABTM relationship

Excuse me if I'm being too much of a beginner but none of the other related answers worker.
I want to show the category name that the links belong to instead of the id.
Here's the migration.
class CreateCategoriesLinks < ActiveRecord::Migration
def self.up
create_table :categories_links, :id => false do |t|
t.references :category
t.references :link
end
end
def self.down
drop_table :categories_links
end
end
The categories model
class Category < ActiveRecord::Base
has_and_belongs_to_many :links
end
The links model
class Link < ActiveRecord::Base
has_and_belongs_to_many :categories
end
And here's what's in the links controller under index and show
#categories = Category.find(:all, :order => 'name')
and here's what's in the index right now but, I've tried every permutation of this that I could find.
<%= link.category.name %>
If it put <%= link.category_ids %>, it'll show the ids.
Try:
<% link.categories.each do |cat| %>
<%= cat.name %><br>
<% end %>