I have many images(add_images_to_steps) belonging to steps which belongs to diys. All information saves correctly as DB browser shows, but i have problem viewing images.
with
views/diys/show.html.erb
<p id="notice"><%= notice %></p>
<h2><%= #diy.summary %></h2>
<% #steps.each do |step| %>
<p><%= step.step_content %></p>
<% step.add_images_to_steps.each do |i| %>
<%= image_tag i.image_url.to_s %>
<% end %>
<% end %>
I get NoMethodError in Diys#Show
undefined method `image_url' for #
and if I change
to
I get this
-----------------------------------------------------------------------
Heres My migrations, models and controllers.
-----------------------------------------------------------------------
diys_controller.rb
class DiysController < ApplicationController
before_action :set_diy, only: [:show, :edit, :update, :destroy]
def show
#diy = Diy.find(params[:id])
#steps = #diy.steps.all
#diy.add_images_to_steps.all
end
def new
#diy = Diy.new
#step = #diy.steps.new
#step.add_images_to_steps.new
end
...
def diy_params
params.require(:diy).permit(:title, :summary, :tip, :warning, steps_attributes: [:step_content, add_images_to_steps_attributes: [:image]])
end
models/diy.rb
class Diy < ActiveRecord::Base
has_many :steps, dependent: :destroy
accepts_nested_attributes_for :steps, reject_if: :all_blank
has_many :add_images_to_steps, :through => :steps, dependent: :destroy
accepts_nested_attributes_for :add_images_to_steps
end
models/step.rb
class Step < ActiveRecord::Base
belongs_to :diy
has_many :add_images_to_steps, dependent: :destroy
accepts_nested_attributes_for :add_images_to_steps
mount_uploader :image, ImageUploader
end
models/add_images_to_step.rb
class AddImagesToStep < ActiveRecord::Base
belongs_to :step
end
Diy migration
class CreateDiys < ActiveRecord::Migration
def change
create_table :diys do |t|
t.string :title
t.text :summary
t.text :tip
t.text :warning
t.timestamps null: false
end
end
end
Steps migration
class CreateSteps < ActiveRecord::Migration
def change
create_table :steps do |t|
t.belongs_to :diy
t.text :step_content
t.timestamps null: false
end
end
end
add_images_to_step migration
class CreateAddImagesToSteps < ActiveRecord::Migration
def change
create_table :add_images_to_steps do |t|
t.belongs_to :step
t.string :image
t.timestamps null: false
end
end
end
I figured it out!!
Had to add
mount_uploader :image, ImageUploader
to ../models/add_images_to_step.rb
change def store_dir to
"#{Rails.root}/public/uploads/"
in ../app/uploaders/image_uploader.rb
and
"<%= image_tag i.image_url.to_s %>"
was right for show view.
Related
I am new to Rails and working on a blog project.
I have articles and categories. Each article can have multiple categories and each category can belong to multiple articles.
I also have a join table articles_categories
My migrations:
class CreateArticles < ActiveRecord::Migration[5.2]
def change
create_table :articles do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration[5.2]
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
class CreateArticlesCategoriesJoinTable < ActiveRecord::Migration[5.2]
def change
create_join_table :articles, :categories do |t|
t.index :article_id
t.index :category_id
end
end
end
My model associations:
class Article < ApplicationRecord
has_and_belongs_to_many :categories
end
class Category < ApplicationRecord
has_and_belongs_to_many :articles
end
This all makes sense so far.
What I am struggling with now, is how do I add categories when creating a new Article? I want to be able to select pre-defined categories from a list (stored in Category table) through a form.
What model and/or URL (if any) do I use in the form_with helper?
Is the following anywhere close?
<% form_with model: ???, url: ??? do |f| %>
<%= f.collection_select :category_id, Category.order(:name), :id, :name %>
<% end %>
I want to create a Rails app that allows "users" to follow other users. I am semi-new to more complex relationships and am attempting to set up has_many through for the first time. I want friends to be able to follow other users.
Here is my join table:
class Following < ApplicationRecord
belongs_to :user
belongs_to :follower, class_name: "User"
end
Here is my users table:
class User < ApplicationRecord
has_many :followings
has_many :followers, through: :followings
end
Here is my schema:
create_table "followings", force: :cascade do |t|
t.integer "user_id"
t.integer "follower_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I don't know how to set up a form to actually create the relationship. In a users view, I have this, but it doesn't work.
<%= form_for #following do |f| %>
<%= f.hidden_field :follower_id, :value => #user %>
<%= f.select :user_id, #users.collect { |u| [u.name, u.id] } %>
<%= f.submit %>
<% end %>
As I said, I am very new to this type of relationship. I need help. I don't know how to link records through a form.
I am following this tutorial: https://teamtreehouse.com/library/what-is-a-hasmany-through-association-in-ruby-on-rails
I am assuming you have a current_user method that returns the logged in user - like what Devise provides. If not you need to setup authentication first.
Create a nested route:
# config/routes.rb
resources :users, only: [] do
resources :followings, only: [:create, :destroy], shallow: true
end
Add a validation to Following to avoid duplicates:
class Following < ApplicationRecord
belongs_to :user
belongs_to :follower, class_name: "User"
validates_uniqueness_of :user_id, scope: 'follower_id'
end
Add a utility method to User to see if he is following another user:
class User < ApplicationRecord
has_many :followings
has_many :followers, through: :followings
def following?(user)
followings.exist?(user: user)
end
def find_following(user)
followings.find_by(user: user)
end
end
We can then add Follow and Unfollow buttons (they are actually forms) to the /users/show.html.erb view.
<% if current_user.following?(#user) %>
<%= button_to "Unfollow", current_user.find_following(#user), method: :delete %>
<% else %>
<%= button_to "Follow", [#user, #user.followings.new] %>
<% end %>
Note that we don't need any form params since we are using a nested route (POST /users/:user_id/followings) to pass the user id (who gets followed) and we are getting the current user from the session.
We can then setup our controller:
class FollowingsController < ApplicationController
# POST /users/:user_id/followings
def create
#user = User.find(params[:user_id])
#following = Following.new(user: #user, follower: current_user)
if #following.save
redirect_to #user, success: "You are now following #{ #user.name }"
else
redirect_to #user, error: "Could not create following"
end
end
# DELETE /followings/:id
def destroy
#following = Following.find(params[:id])
#following.destroy
redirect_to #following.user, success: "You are no longer following #{ #user.name }"
end
end
I have successfully implemented the dynamic select menus for city and area models in my app.
now I have the following models:
class Pet < ActiveRecord::Base
belongs_to :pet_type
belongs_to :pet_category
belongs_to :pet_breed
end
class PetType < ActiveRecord::Base
has_many :pet_categories, through: :pet_type_categories
has_many :pet_type_categories
end
class PetCategory < ActiveRecord::Base
has_many :pet_types, through: :pet_type_categories
has_one :pet_type_category
end
class PetTypeCategory < ActiveRecord::Base
belongs_to :pet_type
belongs_to :pet_category
has_many :pet_breeds
end
class PetBreed < ActiveRecord::Base
belongs_to :pet_type_category
belongs_to :pet_Type
belongs_to :pet_category
end
migrations:
class CreatePetTypes < ActiveRecord::Migration
def change
create_table :pet_types do |t|
t.string :name
t.timestamps
end
end
end
class CreatePetCategories < ActiveRecord::Migration
def change
create_table :pet_categories do |t|
t.string :name
t.timestamps
end
end
end
class CreatePetTypeCategories < ActiveRecord::Migration
def change
create_table :pet_type_categories do |t|
t.references :pet_type, index: true
t.references :pet_category, index: true
t.timestamps
end
end
end
class CreatePetBreeds < ActiveRecord::Migration
def change
create_table :pet_breeds do |t|
t.string :name
t.references :pet_type_category, index: true
t.timestamps
end
end
end
The pet_type_category table is a join table for pet_types that share the same pet_categories.
So my question is how do I create 3 dynamic select menus of pet_type, pet_category and pet_breed in the create form?
Thanks
Edit: I managed to get the pet type collection_select and pet category grouped_collection_select done once I updated the relationships, now the 3rd one (pet breed) is what I'm stuck at..
I understand now after a bit of a research that it is not possible to have nested groups though surely It should be possible using some kind of helper, however for a lack of a better solution, I added to the PetTypeCategory model:
def pet_type_category_names
"#{self.pet_type.name} #{self.pet_category.name}"
end
and in my view now I have:
<div class="field">
<%= f.collection_select :pet_type_id, PetType.all, :id, :name, {prompt: "Choose your pet's type"} %>
</div>
<div class="field">
<%= f.grouped_collection_select :pet_category_id, PetType.all, :pet_categories, :name, :id, :name, {prompt: "Choose your pet's category"} %>
</div>
<div class="field">
<%= f.grouped_collection_select :pet_breed_id, PetTypeCategory.all, :pet_breeds, :pet_type_category_names, :id, :name, {prompt: "Choose your pet's breed"} %>
</div>
so for the 3rd select instead of having:
Dog
Small
Breed
I have:
Dog Small
Breed
Not sure how to delete the associate between 'fly' and 'invoice'. What I've tried so far deletes the fly from the database
<table class="table table-condensed">
<thead>
<tr>
<th>Invoice Flies</th>
</thead>
<tbody>
<% #invoice.flies.each do |fly| %>
<tr>
<td><%= fly.name %></td>
<td><%= link_to "delete", ??????, method: :delete,
data: { confirm: "You sure?" } %></td>
</tr>
<% end %>
</tbody>
</table>
Invoice Model:
class Invoice < ActiveRecord::Base
attr_accessible :active
validates :user_id, presence: true
belongs_to :user
has_many :categorizations
has_many :flies, through: :categorizations
end
Invoice migration:
class CreateInvoices < ActiveRecord::Migration
def change
create_table :invoices do |t|
t.boolean :active
t.integer :user_id
t.timestamps
end
add_index :invoices, :user_id
end
end
Categorization Model:
class Categorization < ActiveRecord::Base
attr_accessible :fly_id, :user_id
belongs_to :invoice
belongs_to :fly
end
Categorization migration:
class CreateCategorizations < ActiveRecord::Migration
def change
create_table :categorizations do |t|
t.integer :user_id
t.integer :fly_id
t.timestamps
add_index :categorizations, :user_id
add_index :categorizations, :fly_id
end
end
end
Fly Model:
class Fly < ActiveRecord::Base
attr_accessible :description, :name
validates :description, :name, presence: true
has_many :categorizations
has_many :invoices, through: :categorizations
end
Fly migration:
class CreateFlies < ActiveRecord::Migration
def change
create_table :flies do |t|
t.string :name
t.string :description
t.timestamps
end
end
end
You can use dependent: :destroy like this:
class Fly
has_many :categorizations, dependent: :destroy
...
end
This will destroy the categorizations related to that Fly.
Also, remember to use Fly.destroy(:id) (DO NOT USE Fly.delete(:id), or it won't remove the categorizations dependent on that Fly)
I need (or I think) implement polymorphic association in my model but I have something wrong. Let see my situation, it's a simple question/answers system, and the logic is the following:
- a question can be ansewered by N answers.
- An answer can be only a "text" XOR (one or other, not both) a "picture".
Migrations:
class CreateAnswers < ActiveRecord::Migration
def change
create_table :answers do |t|
t.integer :question_id
t.references :answerable, :polymorphic => true
t.timestamps
end
end
end
class CreateAnswerTexts < ActiveRecord::Migration
def change
create_table :answer_texts do |t|
t.text :content
t.timestamps
end
end
end
class CreateAnswerPictures < ActiveRecord::Migration
def change
create_table :answer_pictures do |t|
t.string :content
t.timestamps
end
end
end
Models
*answer.rb*
class Answer < ActiveRecord::Base
belongs_to :user_id
belongs_to :question_id
belongs_to :answerable, :polymorphic => true
attr_accessible :answerable_type
end
answer_text.rb
class AnswerText < ActiveRecord::Base
TYPE = "text"
has_one :answer, :as => :answerable
attr_accessible :content
end
answer_picture.rb
class AnswerPicture < ActiveRecord::Base
TYPE = "picture"
has_one :answer, :as => :answerable
attr_accessible :content
end
Controller
answers_controller.rb:
...
def create
post = params[:answer]
create_answerable(post[:answerable_type], post[:answerable])
#answer = #answerable.answer.new()
end
private
def create_answerable(type, content)
#answerable = ('Answer' + type.capitalize).classify.constantize.new(:content => content)
#answerable.save
end
...
And view form (Only have these fields):
...
<div class="field">
<%= f.label :answerable_type %><br />
<%= select("answer", "answerable_type", Answer::Types, {:include_blank => true}) %>
</div>
<div class="field">
<%= f.label :answerable %><br />
<%= f.text_field :answerable %>
</div>
...
So, the problem is when I submit form I get this error:
undefined method new' for nil:NilClass
app/controllers/answers_controller.rb:52:increate'
Answers? :)
on a has_one relationship, you have to use :
#answerable.build_answer
or
#answerable.create_answer
instead of
#answerable.answer.new
see the reference for more info.