Search from one view through a joined HABTM table - ruby-on-rails

I'm trying to set up a simple search from one view through a joined table and, being a newbie, it's not quite working. I'm testing with the word "books" which I know is in the articles table in the subject column.
My error is:
SQLite3::SQLException: no such column: articles.subject: SELECT COUNT(*) FROM "keywords" WHERE (articles.subject LIKE '%books%')
My schema is:
create_table "articles", force: true do |t|
t.string "title"
t.string "subject"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "articles_keywords", id: false, force: true do |t|
t.integer "article_id"
t.integer "keyword_id"
end
create_table "keywords", force: true do |t|
t.string "keyword"
t.datetime "created_at"
t.datetime "updated_at"
end
My model is:
class Keyword < ActiveRecord::Base
has_and_belongs_to_many :articles
accepts_nested_attributes_for :articles
def self.search_for(query)
where('articles.subject LIKE :query', :query => "%#{query}%")
end
My controller action is:
def index
#keywords = params[:q] ? Keyword.search_for(params[:q]) : Keyword.all
end
My view action is:
<%= form_tag "/", method: "GET" do %>
<%= text_field_tag :q %>
<%= submit_tag "Search" %>
<% end %>
<% if #keywords.any? %>
<% #keywords.each do |k| %>
<section>
<h3><b>Title</b>: <%= link_to k.keyword.title, keyword.title %></h3>
<p><b>Subject</b>: <%= keyword.subject%></p>
<% end %>
Many thanks for any help!

You have to join the asocciated table (and you can use scopes):
class Keyword < ActiveRecord::Base
has_and_belongs_to_many :articles
accepts_nested_attributes_for :articles
scope :search_for, ->(query){ joins(:acticles).where('articles.subject LIKE :query', :query => "%#{query}%")}
end
See RubyOnRails-Guides
A gem that eases complex querying is squeel. With squeel the scope would by
scope :search_for, ->(query){ joins{articles}.where{articles.subject =~ "%#{query}%"} }

Related

Rails association method errors

In my ruby on rails application i've applied a relationship between two table: Article and Category with a relation has_and_belongs_to_many.
class Category < ActiveRecord::Base
has_and_belongs_to_many :articles
end
class Article < ActiveRecord::Base
has_and_belongs_to_many :categories
end
I'm following this tutorial for implement a Has_many system with the checkboxes (Railcast)
i've write this part:
<% for category in Category.all%>
<div>
<%= check_box_tag "article[category_ids][]", category.id, #article.categories.include?(category) %>
<%= category.name %>
</div>
<% end %>
but i've got this error :
Mysql2::Error: Table 'CMS_development.articles_categories' doesn't exist: SHOW FULL FIELDS FROM articles_categories
Where am i wrong?
EDIT ADDING THE MIGRATION AND THE SCHEMA
MIGRATION:
class AddCategoryToArticles < ActiveRecord::Migration
def change
add_reference :articles, :category, index: true, foreign_key: true
end
end
SCHEMA:
ActiveRecord::Schema.define(version: 20151001153131) do
create_table "articles", force: :cascade do |t|
t.boolean "published"
t.boolean "on_evidance"
t.boolean "foreground"
t.string "title", limit: 255
t.string "subtitle", limit: 255
t.datetime "date"
t.text "body", limit: 65535
t.text "small_body", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id", limit: 4
end
add_index "articles", ["category_id"], name: "index_articles_on_category_id", using: :btree
create_table "categories", force: :cascade do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "articles", "categories"
end
Two issues:
You don't have the join table articles_categories
You're not referencing the categories correctly
Firstly, to use a has_and_belongs_to_many association, you need to invoke the appropriate join table:
Your schema shows, quite clearly, that you don't have this. As stated in other answers, you need to create a schema as follows:
class CreateArticlesCategories < ActiveRecord::Migration
def change
create_table :articles_categories, id: false do |t|
t.references :articles
t.references :categories
end
end
end
This will give you the join table which you'll be able to populate with your select box...
--
For your checkbox, you can do the following:
#app/views/articles/new.html.erb
<%= form_for #article do |f| %>
<%= f.collection_select :categories, Category.all, :id, :name %>
<%= f.submit %>
<% end %>
To populate this through the controller, you'd need to use the following:
#app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def new
#article = Article.new
end
def create
#article = Article.new article_params
#article.save
end
private
def article_params
params.require(:article).permit(:title, :categories)
end
end
You should write and execute a migration for creating articles_categories table:
class CreateArticleCategory < ActiveRecord::Migration
def change
create_table :articles_categories do |t|
t.references :articles
t.references :categories
end
end
end

Getting no method error in Customers#index while trying to access attribute of Order model

How to access customer's name from Customer table onto my Order's action view index. And vice versa.
I am getting this error:
NoMethodError in Customers#index.
Undefined method `order' for #Customer:0x24f4...
class Customer < ActiveRecord::Base
has_many :orders, foreign_key: "customer_id"
end
class Order < ActiveRecord::Base
belongs_to :customer
end
In my migration:
create_table "customers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.integer "customer_id"
t.datetime "orderdate"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "orders", ["customer_id"], name: "index_orders_on_customer_id", using: :btree
end
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<%= customer.order.orderdate %>
<% end %>
In orders's index.html.erb
<% #orders.each do |order| %>
<%= order.orderdate %>
<%= order.customer.name %>
<% end %>
you have has many relation with customer and order so
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<!-- this will display customers all order and fetch first and then show that order's orderdate -->
<%= customer.orders.first.orderdate %>
<% end %>
Try a different approach to setting the relationships between your models in the migration:
t.belongs_to :customer, index: true
See http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association

Rails4: How can I display the name in assosiated table?

What I'd like to do is to display the name in the details table.
I tried some codes, but I couldn't.
Please advise me on how to display the value.
Controller code:
class BooksController < ApplicationController
def show
#book = Book.find(params[:id])
#article = Article.where(book_id: (params[:id])).order(day: :asc)
end
end
View code:
<div class="row">
<% #article.each do |a| %>
<%= a.day %><br>
<%= a.title %><br>
# want to display the name in detail table where (details.day = articles.day and details.book_id = articles.book_id)
<% end %>
</div>
Relevant model setup:
class Article < ActiveRecord::Base
belongs_to :Book
default_scope -> { order(day: :asc, start_time: :asc) }
end
class Detail < ActiveRecord::Base
belongs_to :Book
end
class Book < ActiveRecord::Base
has_many :articles
has_many :details
end
Schema:
ActiveRecord::Schema.define(version: 2015099999999) do
create_table "details", force: true do |t|
t.integer "book_id"
t.integer "day"
t.string "name"
t.string "detail"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "articles", force: true do |t|
t.integer "book_id"
t.integer "day"
t.string "start_time"
t.string "end_time"
t.integer "category"
t.string "title"
t.string "contents"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "books", force: true do |t|
t.date "publish_date"
t.string "title"
t.datetime "created_at"
t.datetime "updated_at"
end
end
You want to define an association between Article and Detail using the :through option:
class Article < ActiveRecord::Base
belongs_to :book
has_many :details, through: :book
end
You can also define an accessor to find just the detail for the matching day:
class Article < ActiveRecord::Base
def matching_detail
details.find_by_day day
end
end
Which you can then use in the view:
<% #article.each do |a| %>
<%= a.matching_detail.try(:name) %>
<% end %>
It's good to define the associations for articles like
has_many :details, through: :book
You can also also use this .
Article.includes(:details).where(book_id: (params[:id])).order(day: :asc)
Or
Article.select('articles.name,details.name,..').joins(:details).where([condition])

How to send mail related to a category

So, I have 3 models Quotes, Categories, and Subscribers.
Essentially, it's a newsletter application. Subscribers can select a category and then enter their email and they will receive quotes related to that category via email.
There's two issues here,
1. The category's are stored in a constant in the category model.
so how do I display the category type that the subscriber chose
in text format? I'd like to show in the email
something like "here's your email on <%= category.name %>" which would translate to
"here's your email on Food."
2. I'd like to ONLY send emails about the category that the subscriber subscribed to.
How might I accomplish this? Please provide examples.
This is what I have so far:
Category.rb
class Category < ActiveRecord::Base
belongs_to :quote
belongs_to :subscriber
CATEGORY_TYPE = {
1 => "Food",
2 => "Fitness",
3 => 'Cats',
}
end
Quote.rb
class Quote < ActiveRecord::Base
has_many :categories
belongs_to :category
validates :title, presence: true
end
Subscriber.rb
class Subscriber < ActiveRecord::Base
has_one :category
validates :email, presence: true
end
schema
create_table "categories", force: true do |t|
t.string "name"
t.integer "quote_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "subscriber_id"
t.integer "category_type"
end
create_table "quotes", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "subscribers", force: true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
Update subscribers_mailer.rb
def SendMyEmail(email, category, quote, subscribers)
#category = category
#quote = quote
#subscribers = subscribers
#email = email
mail to: email, subject: 'New Quotes'
end
end
and then of course:
Category.all.each do |category|
SubscriptionMailer.SendMyEmail("email#test.com", category, category.quotes.first, category.subscribers).deliver
end
Update:
There's two remaining issues here,
1. Categories aren't syncing to subscribers. For example when I run category.subscribers.last
it's nil. and category.subscribers throws an empty array. How can I sync these? I think it has
to do with the fact that subscribers are selecting a category from the Category::CATEGORY_TYPES
constant as seen in the view code below.
2. I'd like to automate it so that these emails are sent to subscribers once a day.
How might I do this?
view code (subscribers/new.html.erb:
<div class="styled email-input2">
<%= form_for #subscriber do |f| %>
<% if #subscriber.errors.any? %>
<div class="errorExplanation" style="color: white;">
<h2><%= pluralize(#subscriber.errors.count, 'error') %> encountered:</h2>
<ul>
<% #subscriber.errors.full_messages.each do |m| %>
<li><%= m %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :subscriber, #subscriber.build_category do |cat| %>
<%= cat.select(:category_type, Category::CATEGORY_TYPE.map{|p| [p[1], p[0]]}, {prompt: 'Please select'}, {class: 'styled email-input2'}) %>
<% end %>
</div>
Ok first lets modify the migrations:
create_table "categories", force: true do |t|
#DELETE QUOTE_ID AND SUBSCRIBER ID
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "category_type"
end
create_table "quotes", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.references :category #this will add integer category_id
end
create_table "subscribers", force: true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.references :category #this will add integer category_id
end
Next we will modify the models to reflet the changes in the migration:
class Category < ActiveRecord::Base
has_many :quotes
has_many :subscribers
CATEGORY_TYPE = {
1 => "Food",
2 => "Fitness",
3 => 'Cats',
}
end
class Quote < ActiveRecord::Base
belongs_to :category
validates :title, presence: true
end
class Subscriber < ActiveRecord::Base
belongs_to :category
validates :email, presence: true
end
Now you can get the subscribers for a cateogyr using the following:
category = Category.find(1) #use whatever id you want
category.subscribers #list of all subscribers for a category
Getting the quotes for a category is similarly straight forward:
category = Category.find(1)
category.quotes
So assuming your mailer takes a category, quote and a list of subscribers for the email to send
Category.all.each do |category|
Mailer.SendMyEmail(category, category.quotes.first, category.subscribers).deliver
end
In the "SendMyEmail" function in your mailer you will have
def SendMyEmail(category, quote, subscribers)
#YOUR CODE FOR TYHE VIEW HERE
end

Rails: How to get one model to "belong" to another model?

I have a model "votes" which belongs_to two models by polymorphous association, and has the attributes user_id and comment_id. Previously, I had a voting system in place for users that would create a new vote for a specific user every time a button was pressed:
<%= form_for [#user, #vote] do |f| %>
<input type="hidden" id="user_id" name="user_id" value="#{#user.id}" />
<%= f.submit ": )", :onclick => 'alert("Voted up!")' %>
<% end %>
and #user.votes.count would return the number of times the button was pressed. However, I switched to a different method:
View:
<%= link_to "voteuser", vote_user_path(#user.id), method: :post, :class => "btn btn-small" %>
Controller:
def vote
#user = User.find(params[:id])
Vote.create!(user_id: #user.id)
redirect_to #user
end
Routes:
Website::Application.routes.draw do
root 'home_page#home'
get "votes/new"
get 'users/random'
post 'users/vote/:id' => 'users#vote', as: 'vote_user'
get 'users/users/random' => 'users#random'
resources :users
get "all/allusers"
get "all/users/new" => 'users#new'
get 'all/all/allusers' => 'all#allusers'
end
and a Vote is still created, with a user_id equal to the current User.id, but now #user.votes.count returns 0, so the application isn't registering that the vote belongs to the user. How can I remedy this?
Vote Model:
class Vote < ActiveRecord::Base
belongs_to :voteable, polymorphic: true
end
Votes Schema:
create_table "votes", force: true do |t|
t.integer "thing_id"
t.integer "comment_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "voteable_id"
t.string "voteable_type"
end
User Model:
class User < ActiveRecord::Base
has_many :votes, as: :voteable
end
User Schema:
create_table "users", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
end
It should be Vote.create!(voteable_id: params[:id], voteable_type: 'User') for users or in short
Vote.create!(vote: User.find(params[:id])) # Vote.create!(vote: Comment.find(params[:id]))
check if you retrieve the user id in your controller and you can in your view
<%= form_for [#user, #vote] do |f| %>
<%= f.hidden_field :user_id, value: f.model.user.id %>
<%= f.submit ": )", :onclick => 'alert("Voted up!")' %>
<% end %>
on your controller you can simply do:
def vote
Vote.create!(user_id: params[:id])
redirect_to #user
end
but you use the polymorphous association so you have to specify the user_id and the user_type.

Resources