Understanding associations in rails 3 - ruby-on-rails

Seems I need to brush up on my associations in rails. At present I am trying to display all posts that have the department name as staff.
two models exist at present, posts and departments
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
end
class Department < ActiveRecord::Base
has_many :posts
attr_accessible :name, :post_id
#Scopes
scope :staff_posts, where(:name => "Staff")
end
So i want to display all posts that have the department name staff
to do this i have put this in my controller
class PublicPagesController < ApplicationController
def staffnews
#staffpost = Department.staff_posts
end
end
In my view i am trying to display all these posts like so
<% #staffpost.each do |t| %>
<h2><%= t.title %>
<h2><%= t.comments %></h2>
<% end %>
Clearly going wrong somewhere as i get undefined method nil, even though i have 3 posts with the name 'Staff'
Can someone please explain where i am misunderstanding the association as would love to get this right
EDIT
Routes
scope :controller => :public_pages do
get "our_news"
match "our_news/staffnews" => "public_pages#staffnews"

In controller it returns department with name staff. And you are using title and comments on on department objects thats why its giving nil method error.
Use like this:
def staffnews
#dept_staff = Department.staff_posts
end
<% #dept_staff.each do |ds| %>
<% ds.posts.each do |p| %>
<h2><%= p.title %></h2>
<h2><%= p.comments %></h2>
<% end %>
<% end %>
or
In post model create named_scope
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
scope :staff_posts, :include => :department, :conditions => {"departments.name" => "Staff"}
end
class Department < ActiveRecord::Base
has_many :posts
attr_accessible :name, :post_id
end
Controller:
def staffnews
#staffpost = Post.staff_posts
end
View: #No change
<% #staffpost.each do |t| %>
<h2><%= t.title %></h2>
<h2><%= t.comments %></h2>
<% end %>

Your staff_posts scope is only selecting the Departments with the name "Staff". Assuming you will have one and only one department named staff, you have a few ways to handle this.
This will find all departments with the name staff, and eager load the posts that go along with it:
#department = Department.where(name: "Staff").include(:posts).first
Since you are trying to scope Post, however, this belongs in Post. Here's an example using a method as scope:
class Post < ActiveRecord::Base
belongs_to :department
attr_accessible :title, :comments, :department_id
def self.staff
where(department_id: staff_department_id)
end
def staff_department_id
Department.find_by_name!("Staff").id
end
end
This way, you can use #staff_posts = Post.staff and iterate over that collection (Note: I don't recommend getting staff_department_id this way permanently. This could be set to a constant when the app boots up, or some other more robust solution).

You can find the all the posts that have the department name staff by following changes:
class PublicPagesController < ApplicationController
def staffnews
#get all the department which have name is staff
departments = Department.where("name=?","staff")
#get all the ids
department_ids = departments.map(&:id)
#retrieve post that department name is staff
#staffpost = Post.find_by_department_id(department_ids)
end
end

Related

Displaying top level category name in view | ruby on rails

I have one blog_categories table with sub_categories based on a parent_id value that points to the main category's .id that is above the subcategory. Main categories have a parent_id of NULL. This is all done through self-referential relations.
How do I display the name of the current sub_category and parent blog_category in the blog_categories.show view based on the /blog_categories/#?
For example;
"News" has .id 1 and parent_id NULL since it's the main category.
"Good" has .id 2 and parent_id 1 since it belongs to the category with an .id of 1.
"Bad" has .id 3 and parent_id 1 since it belongs to the category with an .id of 1.
When /blog_categories/2 is rendered I am trying to have it show the
name of the subcategory ("Good" in this case) followed by its
parent category ("News" in this case).
The desired result is to be a header stating "Good News"
BlogCategory Model:
class BlogCategory < ApplicationRecord
has_many :posts
# This is called a self referential relation. This is where records in a table may point to other records in the same table.
has_many :sub_categories, class_name: "BlogCategory", foreign_key: :parent_id
# This is a scope to load the top level categories and eager-load their posts, subcategories, and the subcategories' posts too.
scope :top_level, -> { where(parent_id: nil).includes :posts, sub_categories: :posts }
end
Blog_categories controller:
class BlogCategoriesController < ApplicationController
def index
#categories = BlogCategory.top_level.includes(sub_categories: :posts)
#category = BlogCategory.find_by_id(params[:id])
unless #category.nil? # Allows for categories to have nothing in them, eliminating the NoMethodError
#sub_category = #category.sub_categories.first
#posts = #subcategory.posts
end
#all_posts = Post.all
end
def show
#category = BlogCategory.find_by_id(params[:id])
#sub_category = #category.sub_categories
#posts = #category.posts
end
private
def cat_params
params.require(:blog_category).permit(:name, :parent_id, :sub_category)
end
end
My Show View:
<% BlogCategory.top_level do |category| %>
<% category.sub_categories do |sub_category| %>
<h2 class="center p-space blog-h2"><%= sub_category.name %> <%= category.name %></h2>
<% end %>
<% end %>
I've tried a few combinations of do statements but I really cant crack this problem. I would love some help in figuring this one out, thanks!
If this helps, I've had some success with <%= #category.name %> but it only shows the sub_category's name.
If I'm not mistaken, you need to display current category name + current category subcategories names. Try to adapt this example to your needs:
# controller
def show
#category = BlogCategory.find(params[:id])
if #category.present?
#sub_cat_names = #category.sub_categories.map(&:name)
#posts = #category.posts
end
end
# view
<% if #category.present? %>
<h2>Current category:</h2> <%= #category.name %>
<h3>Sub categories:</h3> <%= #sub_cat_names.join(', ') %>
<% end %>
UPDATE
I don't understand why do you need #category and #sub_category variables in show action if you don't use them in the view. In accordance with your latest additions, the solution can look like this:
# model
class BlogCategory < ApplicationRecord
has_many :posts
has_many :sub_categories, class_name: 'BlogCategory', foreign_key: :parent_id
belongs_to :parent, class_name: 'BlogCategory'
scope :top_level, -> { where(parent_id: nil).includes :posts, sub_categories: :posts }
end
# controller
def show
#sub_category = BlogCategory.find(params[:id])
#category = #sub_category.parent
end
# view
<h2 class="center p-space blog-h2">
<%= "#{#sub_category.name} #{#category.name}" %>
</h2>

Rails Invoicing App

I want to create an invoice in rails. Invoice can have items and each item will have quantity, tax & price. It's a typical invoice we see everyday.
In order to create an invoice what is the best approach.
What is the common model for invoice and items?
I know Items will be a separate model. But how can we have one view for invoice, which creates both the invoice and items added to it?
What I mean is, Inside a new invoice page, there will be list of the clients, and list of the items , But here i'm not sure how to make the association when i create invoice. Is there any good example that i can follow ?
Please I'd appreciate some Help. Or even just a walk through of the steps i need to follow in order to accomplish that...
Here's my basic ERD
Quite a broad question, here's what I'd do:
#app/models/invoice.rb
class Invoice < ActiveRecord::Base
belongs_to :user
has_many :line_items
has_many :items, through: :line_items
accepts_nested_attributes_for :line_items
end
#app/models/line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :invoice
belongs_to :item
end
#app/models/item.rb
class Item < ActiveRecord::Base
belongs_to :company
has_many :line_items
has_many :invoices, through: :line_items
end
--
#app/models/user.rb
class User < ActiveRecord::Base
has_many :invoices
end
This will be the base level "invoice" association structure - your clients/users can be built on top of it.
Your routes etc can be as follows:
#config/routes.rb
resources :invoices
#app/controllers/invoices_controller.rb
class InvoicesController < ApplicationController
def new
#invoice = current_user.invoices.new
#invoice.line_items.build
end
def create
#invoice = current_user.invoices.new invoice_params
#invoice.save
end
end
Then your view will be something like this:
#app/views/invoices/new.html.erb
<%= form_for #invoice do |f| %>
<%= f.fields_for :line_items do |l| %>
<%= f.text_field :quantity %>
<%= f.collection_select :product_id, Product.all, :id, :name %>
<% end %>
<%= f.submit %>
<% end %>
This would create the corresponding #invoice, with which you'll be able to call as follows:
#user.invoices.first
Apart from this, I don't have anywhere enough specific information to help specifically
May I recommend using the payday gem? I have created invoice models in the past applications and I'll tell you what, it can get pretty tricky sometimes depending on the type of application you're building. But the reason I like using this gem besides the convenience factor is that it can also render your invoices as a customizable PDF.
It makes adding items to the invoice a breeze as well, for example from their GitHub page:
invoice = Payday::Invoice.new(:invoice_number => 12)
invoice.line_items << Payday::LineItem.new(:price => 20, :quantity => 5, :description => "Pants")
invoice.line_items << Payday::LineItem.new(:price => 10, :quantity => 3, :description => "Shirts")
invoice.line_items << Payday::LineItem.new(:price => 5, :quantity => 200, :description => "Hats")
invoice.render_pdf_to_file("/path/to_file.pdf")

How to optimize this code in Ruby on Rails?

I have 3 models source.rb belongs to category.rb and feed_entry.rb belongs to source.rb.
I need to display feed_entries in category
Category name
FeedEntry 1
FeedEntry 2
FeedEntry 3
Now it looks like this
class CategoriesController < ApplicationController
def show
#category = Category.find(params[:id])
#sources = #category.sources.all
end
end
show.html.erb
<%= #category.name %></h4>
<% #sources.each do |source| %>
<% source.feed_entries.each do |feed_entry| %>
<%= link_to feed_entry.name, feed_entry %>
<%= feed_entry.source.title %>
<% end %>
<% end %>
this is very slow
I use mongoid 4, rails 4
Models
class Category
include Mongoid::Document
field :name, type: String
has_many :sources, dependent: :destroy
end
class FeedEntry
include Mongoid::Document
field :name, type: String
belongs_to :source, touch: true
validates :source_id, presence: true
end
class Source
include Mongoid::Document
field :title, type: String
has_many :feed_entries, dependent: :destroy
belongs_to :category, touch: true
end
Some thinks to know :
Never use .all, unless you know size of result data. Always use pagination or limit.
When you have a loop like your each in view, this will call queries like this :
Give me a category
Give me its sources
Give me feed entries for source 1
Give me feed entries for source 2
....
You should eagler load your association like this :
#sources = #category.sources.limit(20).includes(:feed_entries)
It will to theses queries :
Give me a category
Give me its sources
Give me feed entries for theses sources
If you don't want any information about categories (like I think), you should add a relation to your model :
Class Category
has_many :sources
has_many :feed_entries, :through => :sources
end
Then call in your controller
#feed_entries = #category.feed_entries
This will do only ONE query :
Give me category
Give me the feed entries of the category
That's it !
I found a solution:
In Category.rb add feed_entries
class Category
def feed_entries
FeedEntry.in(source_id: sources.map(&:id))
end
end
and in show.html.erb
<% #category.feed_entries.includes(:source).each do |feed_entry| %>
<%= link_to feed_entry.name, feed_entry %>
<%= feed_entry.source.title %>
<% end %>

Why am i getting a NoMethodError on this?

I have this in my config/routes.rb:
get '/:category/:region', to: 'categories#filtered_by_region'
The filtered_by_region action is as shown below:
#filtered_by_region method
def filtered_by_region
#region = Region.where(title: params[:region]).first
#category = Category.where(title: params[:category]).first
#teams = Team.where(region_id: #region.id, category_id: #category.id)
end
I have a view filtered_by_region.html.erb that looks as follows:
Region: <%= #region.title %>
Category: <%= #category.title %>
<% #teams.each do |team|%>
<%=team.title %>
<% end %>
region.rb model is as follows:
class Region < ActiveRecord::Base
has_many :teams
attr_accessible :title
end
category.rb model is as follows:
class Category < ActiveRecord::Base
has_many :teams
attr_accessible :title
end
team.rb model is as shown below
class Team < ActiveRecord::Base
belongs_to :category
belongs_to :region
end
I also have the corresponding regions, teams and categories tables already populated with data.
when i enter a url that looks like this:
http://localhost:3000/football/south_west
i get an error with the following message:
undefined method ``title' for nil:NilClass I have realized both the #region and #category are returning nil but i do not understand why. i do have a category with football title and a region with south_west title in categories and regions tables respectively.
Why don't you use find_by (if you're using Rails 4) or find_by_title (if you're using Rails 3):
def filtered_by_region
#category = Category.find_by_title(params[:category])
#region = Region.find_by_title(params[:title])
if defined?(#category) && defined?(#region)
#teams = Team.where(region_id: region.id, category_id: category.id)
else
redirect_to root_path
end
end
I think the likely issue will either be your query is not finding any records, or you'll be trying to access a collection as a record (regardless of the use of .first)

Rails: Sunspot text searching with model associations, using :through

How do I search with associations and through with sunspot?
class StaticController < ApplicationController
def search
#search = Sunspot.search Business, Service do
fulltext params[:q]
paginate :per_page => 10
order_by_geodist(:location, *Geocoder.coordinates(params[:loc]))
end
#biz = #search.results
end
class Business < ActiveRecord::Base
attr_accessible :name
has_many :services, :through => :professionals
searchable do
text :name #name in business column
# how to do I get the services?
end
end
class Service < ActiveRecord::Base
attr_accessible :service
belongs_to :professional
end
class Professional < ActiveRecord::Base
belongs_to :business
has_many :services, as: :servicable
end
In the view, I have this (lots of looping)
<%= #biz.each do |b| %>
<%= b.name %>
<!-- looping through professionals model -->
<% b.professionals.each do |prof| %>
<!-- looping through services model -->
<% prof.services.each do |s| %>
<%= s.service %>
<% end %>
<% end %>
<% end %>
This works if I search for a name that is within the business model, but what if I'm searching through a term that's in the Service model? It won't display correctly because my view is only coming from the business side. How do I make it so the business name will pop up if I search through Service model?
Thanks
You will need to make additional indexes for the associated models in the calling model to make this happen. For example:
class Business < ActiveRecord::Base
attr_accessible :name
has_many :services, :through => :professionals
searchable do
text :name #name in business column
text :services do # this one for full text search
services.map(&:service).compact.join(" ")
end
string :services , :multiple => true do #this one for exact searches
services.map(&:service).compact
end
end
end
After that you can do queries like:
Bussines.search do
with(:services, "some_service")
end.execute.results
Now you no longer have to do join on mysql tables to fetch data. You can just fetch data from the solr. This is one of biggest advantages of solr.
I hope this makes it clear. Fell free to drop a comment if you need more details.

Resources