has_many join display issue - sql

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>

Related

Rails creating a collection without an association

What is the best and correct way to setup a simple_forms_for field that contains a collection for a select field but the values to be contained in the select need to be sourced from a model that does not have a direct association to the calling fields model?
For example I have a simple_forms_for form like follows:
<%= simple_form_for(#customer) do |f| %>
<%= f.error_notification %>
<fieldset>
<div class="form-inputs">
<%= f.input :code, required: true %>
<%= f.input :name, required: true %>
<%= f.input :location, required: true %>
<%= f.input :service_level %>
<%= f.input :golive_date, :as => :date_picker %>
<%= f.input :connection_type %>
<%= f.input :service_centre %>
<%= f.input :end_date, :as => :date_picker %>
<%= f.input :sla %>
<%= f.input :project_code %>
</div>
<div class="form-actions">
<%= f.button :submit, :class => "btn btn-primary" %>
</div>
</fieldset>
<% end %>
I want to make the :service_level field a selection field and add a collection to it, however the table that stores the lookup values is not associated with the Customer table for the form.
class Lookup < ActiveRecord::Base
validates_presence_of :name, :description
has_many :lookup_values
accepts_nested_attributes_for :lookup_values, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class LookupValue < ActiveRecord::Base
belongs_to :lookup
end
class CreateLookups < ActiveRecord::Migration
def change
create_table :lookups do |t|
t.string :name
t.string :description
t.timestamps
end
end
end
class CreateLookupValues < ActiveRecord::Migration
def change
create_table :lookup_values do |t|
t.integer :lookup_id
t.string :name
t.string :value
t.timestamps
end
end
end
I basically want to be able to populate the values of the select using the following SQL query:
select v.name||' - '||v.value
from lookup_values v,
lookups l
where v.lookup_id = l.id
and l.name = 'Service level';
The actual value that is saved into the :service_level field needs to be the value of v.name.
All of the collections examples I have seen only appear to show how to create selects based on models that have an association between them, just wondering if there is an easy way to achieve this without an association.
Ok, well this is embarrassing...
Simple solution was to modify the _form.html.erb view file so that the :service_level field reads as:
<%= f.input :service_level, :collection => LookupValue.joins(:lookup).where(lookups: { :name => 'Service level' }).pluck(:name) %>
I probably need to make this more DRY when repeating for multiple lookup values in the form.
Any ideas how I can enhance this code to:
Remove the blank value that is listed in the select field drop down?
Modify the value text that displays in the select drop down to be
Name ||' - '||value. For example show the values in the format
"L1 - Level 1". The actual value selected and saved would need to
remain as "L1" (the :name value)

How to handle each iterator?

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)

Country name not saving to table or showing in view rails 3

I have a simple app where you can upload recipes
I have just integrated the gem terrarum to give me all the countries of the world within my country model. I have a relationship where a recipe has_one country and a country belongs to recipe. I am using nested forms from ryan bates and have had no problem getting information to show from my ingredients model and preparation model. But i cannot get the country name to save in the table or show in the view (though this is caused by not saving to model)
Code is as follows
Form
<%= f.label :country_id, "Country Of Origin" %>
<%= f.collection_select(:country_id, Country.all, :id, :name, :prompt => 'Please select country') %>
View
<% #recipes.each do |r| %>
<tr>
<td><%= r.dish_name %></td>
<td><%= r.country.name %></td>
<td><%= r.difficulty %></td>
<td><%= r.preperation_time %></td>
<td><%= ingredient_names(r.ingredients) %></td>
<td><%= preperation_steps(r.preperations) %></td>
<td><%= image_tag r.avatar.url(:thumb)%></td>
</tr>
Helper
def preperation_steps(preperations)
if preperations
preperation_array = preperations.map {|pre| pre.prep_steps}
preperation_array.join("\n")
end
end
def country_name(country)
if country
country_array = country.map {|c| c.country_name}
country_array.join("\n")
end
end
end
I have included my preparation helper as this works, so surely my country_name helper mirrors this? or do i not need to put a helper in for this?
recipe controller
def new
#recipes = current_user.recipes if current_user.recipes #show recipes if the user has any recipes
#favourites = current_user.favourites
end
recipe model
belongs_to :user
belongs_to :country
has_many :ingredients
has_many :preperations
has_many :favourites
attr_accessible :dish_name, :difficulty, :preperation_time, :ingredients_attributes, :preperations_attributes, :country_id, :avatar:preperations_attributes, :country_id, :avatar
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
accepts_nested_attributes_for :ingredients, :preperations
scope :top_countries, order("country_of_origin DESC")
if anyone can help it would be much appreciated
Thanks
In this code I see two mistakes:
Recipe should belong_to :country. Don't use has_one here. The foreign key country_id should be in the recipes table.
#recipe.build_country is not necessary. You already have a list of countries. You should only use build_country if you are planning to add a new country to the list of countries, which you are not in this case.
Also, you don't need fields_for. You can just do:
<%= f.label :country_id, "Country Of Origin" %>
<%= f.collection_select(:country_id, Country.all, :id, :name, :prompt => 'Please select country') %>

Setting up Authlogic I keep hitting: "undefined method `login' for #<UserSession: no credentials provided>"

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.

Using :joins in Ruby on Rails 3

Greetings
I have 3 db tables:
Types
id
name
Sources
id
name
type_id
Operations
id
source_id
comment
...
and models for each:
class Type < ActiveRecord::Base
has_many :sources, :dependent => :destroy
end
class Source < ActiveRecord::Base
belongs_to :type
has_many :operations, :dependent => :destroy
end
class Operation < ActiveRecord::Base
belongs_to :source
default_scope :order => 'created_at DESC'
end
In Operation#index controller i have code for getting data (generated by scaffolding)
#operations = Operation.all
And piece of view operations/index.html.erb also generated by scaffolding
<% #operations.each do |operation| %>
<tr>
<td><%= operation.source_id %></td>
<td><%= operation.comment %></td>
</tr>
<% end %>
Now I want to use source.name instead of *operation.source_id*
I tried to do:
-replace operation.source_id to operation.sources.name # doesn't work
-tried to using :joins, and can't get Sources table fields
irb(main):057:0> Operation.first( :joins => :source )
=> #<Operation id: 2088, source_id: 1, summ: 10.0, comment: "", created_at: "2011-01-01 07:39:45", updated_at: nil>
or
irb(main):063:0> Operation.first( :joins => 'INNER JOIN sources ON operations.source_id = sources.id' )
=> #<Operation id: 2088, source_id: 1, summ: 10.0, comment: "", created_at: "2011-01-01 07:39:45", updated_at: nil>
How I must properly use :joins for get additional field?
Or there is another approach to get combined tables data.
And why in the operations/show.html.erb i can use <%= #operation.source.name %> and successfully get source.name, but in the *operations/index.html.er*b can't
<% #operations.each do |operation| %>
<tr>
<td><%= operation.source.name %></td>
<td><%= operation.comment %></td>
</tr>
<% end %>
I'd also recommend changing your #index method to use an includes statement to avoid an N+1 situation (i.e. running a separate database query for each individual operation's source).
#operations = Operation.includes(:source).all
Solved:
opearions#index
#operations = Operation.all :joins => :source, :select => '*'
operations/index.html.erb
<% #operations.each do |operation| %>
<tr>
<td><%= operation.name %></td>
...