accessing model that belongs_to another model - ruby-on-rails

I have a model 'Galleries', which has the 'has_many' relationship with 'Exhbition Images' (and opposingly the belongs_to).
I want to display the images associated with each gallery via the Gallery model. However I am encountering the error
undefined method `exhibition_images' for #<Gallery::ActiveRecord_Relation:0x00000108159dd0>
when i try to implement this line of code
gallery index:
<%= #gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
I am sure this is very simple and I am missing something obvious.
Galleries controller
def index
#gallery = Gallery.all
end
Gallery model
class Gallery < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
belongs_to :guide
has_many :exhibition_images
accepts_nested_attributes_for :exhibition_images
end
Exhibition Images
class ExhibitionImage < ActiveRecord::Base
belongs_to :gallery
has_attached_file :image, styles: { small: "100x100", guide: "500x500" }
validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
end
SCHEMA
create_table "exhibition_images", force: true do |t|
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "gallery_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "galleries", force: true do |t|
t.string "title"
t.string "slug"
t.integer "guide_id"
t.datetime "created_at"
t.datetime "updated_at"
end

undefined method `exhibition_images' for #<Gallery::ActiveRecord_Relation:0x00000108159dd0>
You are trying to access exhibition_images on an ActiveRecord::Relation. You have a relation object because you're calling:
#gallery = Gallery.all
That gives you a scope that you can use to chain together more query conditions. You need to get an individual gallery object to be able to call exhibition_images. You can do this by calling first on #gallery, or you need to execute the query and iterate through the results:
<%= #gallery.each do |gallery| %>
gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
<% end %>
If you do that, however, I'd be sure to eager load the exhibition_images:
#gallery = Gallery.includes(:exhibition_images).all

The problem is that #gallery contains an array of Gallery objects.
In your controller you should have:
#galleries = Gallery.all
And then iterate over the #galleries array
<% for gallery in #galleries %>
<%= gallery.exhibition_images.each do |exhibition_image| %>
<%= exhibition_image.title %>
<% end %>
<% end %>

Related

Why am I seeing a hash rendered to the DOM?

I am building a Portfolio website that has a simple view file for projects I have worked on. On my "work" view I render a collection of "technologies" that I used to build a particular product. This is working great, and each technology renders just fine. However, right below the that renders the technologies the plain hash is also being rendered, and I cannot figure out why.
I am following a tutorial for this and I have double checked that my code is the same as the instructor's.
Work View
<%= image_tag #work_item.main_image unless #work_item.main_image.nil? %>
<h1>Title: <%= #work_item.title %></h1>
<em><%= #work_item.subtitle %></em>
<p><%= #work_item.body %></p>
<h2>Technologies Used:</h2>
<%= #work_item.technologies.each do |t| %>
<p><%= t.name %></p>
<% end %>
Schema
create_table "technologies", force: :cascade do |t|
t.string "name"
t.bigint "work_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["work_id"], name: "index_technologies_on_work_id"
end
controller method being used via def show via a before_action
def set_work
#work_item = Work.find(params[:id])
end
Technology Model
class Technology < ApplicationRecord
belongs_to :work
end
Work Model
class Work < ApplicationRecord
has_many :technologies
accepts_nested_attributes_for :technologies,
reject_if: lambda { |attrs| attrs['name'].blank? }
include Placeholder
validates_presence_of :title, :body, :main_image, :thumb_image
def self.react
where(subtitle: "React")
end
scope :ruby_on_rails, -> { where(subtitle: "Ruby on Rails") }
after_initialize :set_defaults
def set_defaults
self.main_image ||= Placeholder.image_generator(height: 600, width: 400)
self.thumb_image ||= Placeholder.image_generator(height: 350, width: 200)
end
end
Here is a screenshot of what I'm seeing
https://imgur.com/a/2T3SRZv
Because the = in
<%= #work_item.technologies.each do |t| %>
indicates that you want #work_item.technologies to be output to the view.
Instead, use
<% #work_item.technologies.each do |t| %>
BTW, that's not a hash, it's an enumerable.

Rails Access data from a different Model within a Controller

UPDATE: Problem solved, thanks to Sebastian and Gabriel for the helpful pointers.
The relevant changes to my code are as follows:
app/controllers/pomodoro_cycles_controller.rb
def pomodoro_collections
{
pomodoro_collection_0: Pomodoro.offset(0).first(100),
pomodoro_collection_1: Pomodoro.offset(100).first(100)
}
end
app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p>
<%= pomodoro.id %>
<%= pomodoro.color %>
</p>
<% end %>
<% end %>
NOTA BENE:
The #first method in ActiveRecord returns an Array, so the keys in my original Hash were nested Arrays. Instead, the following was sufficient to return an Array of Pomodoro objects:
Pomodoro.offset(0).first(100)
DESCRIPTION OF ORIGINAL PROBLEM
Rails 5, PostgreSQL
PROBLEM: I cannot access Pomodoro.all from within PomodoroCycleController
I have two scaffolds: Pomodoro and PomodoroCycle, and I want to access the full list of Pomodoros within the PomdoroCycle controller.
The following code is kept simple, in order to make as clear as possible what I'm trying to do. If I can do these things, then I'll be able to do much more, but one step at a time.
Regarding the db migration files, I have already run bundle exec rails db:migrate
I want to display a full list of Pomodoros in the PomodoroCycle Show View (later to be displayed in Index), but I don't know what is missing.
From app/controllers/pomodoro_cycles_controller.rb
def show
#pomodoros_collections = pomodoro_collections
end
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
From app/views/pomodoro_cycles/show.html.erb
<% #pomodoros_collections.each do |collection| %>
<p><%= collection %></p>
<% end %>
However, this displays nothing in the browser.
app/models/pomodoro_cycle.rb
class PomodoroCycle < ApplicationRecord
has_many :pomodoros
end
app/models/pomodoro.rb
class Pomodoro < ApplicationRecord
belongs_to :pomodoro_cycle
end
Updated db/migrate/20180103032759_create_pomodoro_cycles.rb:
class CreatePomodoroCycles < ActiveRecord::Migration[5.1]
def change
create_table :pomodoro_cycles do |t|
t.string :activity
t.integer :iteration
t.integer :matrix_side_length
t.datetime :created_at
t.datetime :completed_at
t.string :category_labels, array:true, default: []
t.string :category_colors, array:true, default: []
t.string :username
t.timestamps
end
create table :pomodoros do |t|
t.belongs_to :pomodoro_cycle, index: true
t.datetime :completed_at
t.timestamps
end
add_index :pomodoros, :pomodoro_cycle_id
end
end
Untouched db/migrate/20180103054425_create_pomodoros.rb
class CreatePomodoros < ActiveRecord::Migration[5.1]
def change
create_table :pomodoros do |t|
t.boolean :status
t.string :category
t.string :color
t.datetime :completed_at
t.string :username
t.timestamps
end
end
end
First of all, as #SebastianPalma pointed out in the comments, the syntax is wrong
def pomodoro_collections
{
pomodoro_collection_0 => [Pomodoro.offset(0).first(100)],
pomodoro_collection_1 => [Pomodoro.offset(100).first(100)]
}
end
should be:
def pomodoro_collections
{
pomodoro_collection_0: [Pomodoro.offset(0).first(100)],
pomodoro_collection_1: [Pomodoro.offset(100).first(100)]
}
end
Make the keys in the hash symbols
Then to display each Pomodoro put something like:
<% #pomodoros_collections.each do |pomodoros_collection_hash| %>
<h2><%= pomodoros_collection_hash[0] %></h2>
<% pomodoros_collection_hash[1].each do |pomodoro| %>
<p><%= pomodoro.id %></p> #Or the attribute you want to display
<% end %>
<% end %>
Hope this help

undefined method `image' for #<Array> when using group_by

Hi can anyone point me to the right direction? I´m trying to show images on views/pages/index.html.erb the images are uploaded on views/products/new.html.erbthrough the _form.html.erbpartial. Each product/picture then belongs to a category which I can select in the _navbar.html.erb and is then directed to the views/categories/show.html.erbto see pictures of each product in that category and so on.
That is all working fine
But now I want to display the last added picture in each category on the views/pages/index.html.erb and I´m always getting this error : undefined method 'image' for #<Array:0x007f8d1fb19ff0>
I´m pretty lost at the moment, and hopefully someone can guide me to the right path.
My code id like this:
pages_controller.rb
class PagesController < ApplicationController
def index
#products = Product.all.order(created_at: :desc).group_by(&:category_id)
end
def about
end
def location
end
def stockists
end
end
views/pages/index.html.erb
<% #products.each do |product| %>
<div class="col-lg-3 col-sm-6 col-xs-12 center-block " >
<%= image_tag product.image.url(:medium) %>
<p><%= product.name %></p>
<p><%= product.category.name %></p>
<% end %>
</div>
And then I have, the products.rb and category.rb
product.rb
class Product < ActiveRecord::Base
mount_uploader :image, ImageUploader
validates_presence_of :name, :price
validates_numericality_of :price
belongs_to :category
end
category.rb
class Category < ActiveRecord::Base
has_many :products
end
this as part of the schema.rb
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id", default: 1
end
add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree
and in the end there is this part
add_foreign_key "order_items", "orders", on_delete: :cascade
add_foreign_key "order_items", "products"
add_foreign_key "orders", "users", on_delete: :cascade
add_foreign_key "products", "categories"
end
You are using group_by in the controller, an enumerable method that returns a hash of Product arrays keyed by category_id.
#product = {
:category1 => [#<Product category_id=1>, #<Product category_id=1>, ...],
:category2 => [#<Product category_id=2>, #<Product category_id=2>, ...]
}
When you loop through #products in the view, you are looping through a hash where each iteration is passing an array.
The product variable does not contain a product, but an array of products.
<% #products.each do |product| %> # product is type Array!
<%= image_tag product.image.url(:medium) %> # Array.image throws an error!
<% end %>
You must create an outer loop to step through the hash.
<% #products.each do |category, products| %>
<% products.each do |product| %>
# do stuff
<% end %>
<% end %>

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

Search from one view through a joined HABTM table

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}%"} }

Resources