I'm new to rails and and I'm on the urge of learning Associations.
I'm using Rails version 3.
I have a user model and post model.My need is as below:-
Models
class User < ActiveRecord::Base
has_many :post
end
class Post < ActiveRecord::Base
belongs_to :user
validates_associated :user
end
Schema
ActiveRecord::Schema.define(:version => 20101016171256) do
create_table "posts", :force => true do |t|
t.integer "sell_or_buy"
t.string "title"
t.text "body"
t.integer "user_id" <<<<<<< I thought this will help to associate to user model.
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.string "password"
t.integer "rank"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I thought keeping a user_id field and the belongs_to association will do my job, but
when i tried to display all the posts belonging to a user as follows:
<%= #user.posts %>
in my show.html.erb file. But I get only the following display:-
Name: saran
Email: saran.saran007#gmail.com
Password: abcd
Rank:
Edit | Back
Posts
#<Post:0xb69f47f8>#<Post:0xb69f3024>
I want to display the associated posts "title" and "body" in a readable format.
Also I'm able to create a post with a user_id in which no user exists!. The validates_associated :user is also not working, Please help me out.
Its
class User
has_many :posts
end
Not
has_many :post
Edit and Update your results.
You are getting the posts as expected in your view... So I'm not sure I understand that part of your question. As to the other part, validates_associated just ensures that the attached object is valid itself, and not if it exists at all. For that you want validates_presence_of. See the docs.
I wrote the following partial for my purpose and it works well :).
Thanks for all your inputs.
<% for post in #user.posts do %>
<h3> <%= post.title %> </h3>
<%= post.body %>
<% end %>
Related
Quick question - I have two model classes - Transactions and Accounts.
The Account Model looks as follows:
create_table "accounts", force: :cascade do |t|
t.string "account_name"
t.integer "account_number"
t.boolean "current_asset"
t.boolean "non_current_asset"
t.boolean "current_liability"
t.boolean "non_current_liability"
t.boolean "equity"
t.boolean "cost_of_sales"
t.boolean "operating_expense"
t.boolean "sales"
t.boolean "other_income"
t.boolean "bank"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
The Transaction Model looks as follows:
create_table "transactions", force: :cascade do |t|
t.date "date"
t.string "description"
t.string "reference"
t.integer "amount"
t.integer "account_id"
t.boolean "payment"
t.boolean "receipt"
t.integer "bank_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "vat"
t.integer "vat_amount"
t.integer "transaction_form_id"
t.integer "contact_id"
end
A user adds a new transaction which, through collection_select
they to choose an Account which displays all the accounts except for where bank == true (where bank == true it represents a bank account).
When a user selects a Bank, they can only choose a bank account - where bank==true (again through collection select).
I need to run a method that allows me to call all the Transaction amounts through the Account model based on the bank_id and not the account_id. So how would i associate two different columns of my Transaction model to the same column in the Accounts class through either account_id or bank_id.
The method will look something like:
<% Account.each do |account| %>
<% if account.bank == true && account.transaction(:bank_id) == account.id %>
<%= account.number %>
<%= account.number %>
<%= account.transactions.sum(:amount) %>
<% end %>
Ps: I know this is in my views, not controller, but that's for another discussion!
An image of Models example
I suggest you back up and re-think your approach.
A bank account and a GL account are very different things (even though they both use the word 'account'). Using a single model to represent both of these will lead to confusion and a lot of empty fields. Consider using a BankAccount and a GLAccount (you can use custom inflection on GLAccount, if you like, so you can do gl_account).
If you use BankAccount and GLAccount, then you can simply do a polymorphic association on your Transaction model, something like:
class Transaction < ActiveRecord::Base
belongs_to :account, polymorphic: true
end
You would need to change your transactions table to include account_type and account_id for that to work.
Then, your BankAccount and GLAccount models would look something like:
class BankAccount < ActiveRecord::Base
has_many :transactions, as: :account
end
and
class GLAccount < ActiveRecord::Base
has_many :transactions, as: :account
end
In which case, to iterate the BankAccounts, you would do something like:
<% BankAccount.all.each do |bank_account| %>
<%= bank_account.number %>
<%= bank_account.transactions.sum(:amount) %>
<% end %>
That'll end up with an N+1 query problem, but you can sort that out separately.
BTW, on your GL Accounts, you should use enums for the account type. As you currently have it set up, you will always have 8 empty boolean fields (not good). That might look something like:
class GLAccount < ActiveRecord::Base
has_many :transactions, as: :account
enum account_type: {
current_asset: 0,
non_current_asset: 1,
current_liability: 2,
non_current_liability: 3,
equity: 4,
cost_of_sales: 5,
operating_expense: 6,
sales: 7,
other_income: 8
}
end
Your gl_accounts table would need to include account_type as an integer for that to work.
A couple of other random notes:
if account.bank == true should just be if account.bank
You repeat <%= account.number %>
Given, however, what you have done, you could do something like:
class Account < ActiveRecord::Base
has_many bank_transactions, class_name: 'Transaction', foreign_key: :bank_id
end
In which case, you should be able to do:
<% Account.where(bank: true).each do |bank_account| %>
<%= bank_account.number %>
<%= bank_account.bank_transactions.sum(:amount) %>
<% end %>
I made 'hospital_review' table and 'hospital_review_comments' table with 1:N relationship on ruby on rails.
'hospital_review' table's migration file is like this.
class CreateHospitalReviews < ActiveRecord::Migration
def change
create_table :hospital_reviews do |t|
t.string :written_time
t.string :writter
t.integer :user_id
t.string :category
t.string :hospital_name
t.text :content
end
end
and 'hospital_review_comments's one is like this.
def change
create_table :hospital_review_comments do |t|
t.integer :user_id
t.integer :post_id
t.string :writter
t.string :written_time
t.text :content
t.timestamps null: false
end
end
'hospital_review' table's model file is like this.
belongs_to :user
has_many :hospital_review_comments
'hospital_review_comments' table's one is like this.
belongs_to :user
belongs_to :hospital_review
I wanted to show each hospital review and the comments that are written on it, so I programmed the codes below in 'show.html.erb'.
<% #post.hospital_review_comments.each do |comment| %>
<p><strong><%=comment.user.username%></strong> <%=comment.content%></p>
and this is show action in controller file.
def show
#post = HospitalReview.find(params[:id])
#hospital_comment_writer = User.where(id: session[:user_id])[0]
end
but the error occcured with message
'SQLite3::SQLException: no such column:'.
I tried 'foregin_key' in hospital_review_comments table's model file, but it didn't work. I can't get the reason the error occurred. Plz help!
You are missing hospital_review_id in your hospital_review_comments table
I think you have wrongly added post_id in hospital_review_comments table, which should be hospital_review_id, that will to the job.
Else you can add the foreign_key option in the association as follows.
has_many :hospital_review_comments, foreign_key: 'post_id'
Working through my first Rails app. It will be used for searching and viewing data on books within certain categories.
Two resources: Categories and Books. I created a many-to-many HMT (has many through) relationship (following this RailsCast), as each category will have many books and each book will belong to more than one category. Here's the relevant controller code:
Category model (category.rb)
class Category < ActiveRecord::Base
has_ancestry
has_many :categorizations
has_many :books, :through => :categorizations
end
Book model (book.rb)
class Book < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
accepts_nested_attributes_for :categories
end
Join model (categorization.rb)
class Categorization < ActiveRecord::Base
belongs_to :book
belongs_to :category
end
And here's the database schema:
schema.rb
create_table "books", force: true do |t|
t.string "title"
t.string "url"
t.integer "rank_overall"
t.integer "rank_category"
t.decimal "price"
t.integer "reviews"
t.decimal "rating"
t.date "published"
t.string "img_cover"
t.string "author"
t.datetime "created_at"
t.datetime "updated_at"
t.string "asin"
t.integer "length"
end
create_table "categories", force: true do |t|
t.string "name"
t.text "content"
t.string "ancestry"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry", using: :btree
create_table "categorizations", force: true do |t|
t.integer "book_id"
t.integer "category_id"
t.integer "rank"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categorizations", ["book_id"], name: "index_categorizations_on_book_id", using: :btree
add_index "categorizations", ["category_id"], name: "index_categorizations_on_category_id", using: :btree
I have a show view that shows each category. In this view I'd like to show the books that are in each corresponding category. I have these simple loops:
.col-1-3
%strong TITLE
%ul
- #books.each do |book|
%li= book.title
.col-1-3
%strong AUTHOR
%ul
- #books.each do |book|
%li= book.author
.col-1-3
%strong ULR
%ul
- #books.each do |book|
%li= book.url
And in my category model I have the following:
def show
#books = Book.order("title").includes(:categorizations).select("books.*")
end
def book_params
params.require(:book).permit(:books => [{:title => [:book_id]}, {:id => [:category_id]}, :author, :url] )
end
Here's the problem, from the loops in my view I'm receiving information from all the books (ie: titles, authors, and urls of all the books in the database) and I only want that from the books that are in that particular category.
I believe I need to change the logic in my categories_controller.rb, is this correct? I realize that I'm selecting all the books .select("books.*") but I don't know how to edit this to conditionally call only the books that match the category of that being displayed in the show view.
Grateful for insights.
Update:
Realized that this information is also quite helpful. When assigning categories to books (upon book creation) I use this code:
= check_box_tag "book[category_ids][]", category.id, #book.category_ids.include?(category.id), id: dom_id(category)
Perhaps it's possible to somehow reference dom_id(category) inside of the category model?
Forgive my "newbishness." I'm a front-end guy who is only now beginning to really venture into the back-end.
One possible solution is to change the show action in categories like this
#books=Category.find(params[:id]).books
Assuming the :id param is the id for the category (which would be the convention if you are in the categories controller).
If you don't need the information about the category and you only need the books, you could do a join (not a include) with categorizations and use a where like categorizations: {category_id: params[:id]}. That's not as clear as the 1st option, but it saves a query if you are not showing info about the category.
i have a (hopefully) simple question that might have been answered before.. i just couldnt find it... well, here we go, should be easy enough.
I have this schema
create_table "items", :force => true do |t|
t.text "description"
t.string "priority"
t.date "date"
t.time "time"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "done"
t.integer "user_id"
end
create_table "users", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
And I have this in my form for adding a new item:
<%= collection_select(:item, :user_id, User.all, :id, :name) %>
Now, it works, data is saved correctly (I already set up the proper correlations). What i want though, is to display in the items index, the name of the person the item is assigned to, instead of just an ID number.
In items/index I have:
<td><%= item.user_id. %></td>
but i rather want something like
item.user.name
only, it won't work - I guess I need some action in my controller.
Can you help me please? :)
EDIT here is some more details:
My models:
class User < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :user
end
Add this to your Items class:
class Items < ActiveRecord::Base
belongs_to :user
end
Then item.user.name should work.
I am trying to learn ruby on rails, writing my own simple app (a to-do list). I now want to add a dropdown menu to select a user to assign the task to.
My schema.rb:
create_table "items", :force => true do |t|
t.text "description"
t.string "priority"
t.date "date"
t.time "time"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "done"
t.string "name"
end
create_table "users", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
Now, in my form I have a field with:
<%= f.collection_select(:user, User.all, :id, :name ) %>
It works as far as displaying my users goes. But, when I try to save, I of course get:
ActiveRecord::AssociationTypeMismatch in ItemsController#create
I already have set up the relationship (users has many task, task has one user). What am I missing?
Many thanks for any help!
You don't have a 'user_id' column in 'items' table.
rails g migration AddUserToItems user_id:integer
rake db:migrate
class Item
belongs_to :user
...
end
class User
has_many :items
...
end
collection_select(:item, :user_id, User.all, :id, :name)
<%= collection_select(:item, :user_id, User.all, :id, :name, {:prompt=>true}) %>
1. 2. 3. 4. 5. 6.
The model
Where you are storing in the model
collect of users
what will be saved
what is displayed
prompt with "please select"
This might help.