Rails - cant add a photo with another model as an index - ruby-on-rails

I'm trying to make a create product page that would allow users to upload photos. So products has_many photos. I got this set up but when a photo is added it should have product_id in a column but when it saves to my database the product_id column is blank.
product controller
def create
#product = current_user.products.build(params[:product])
#photo = current_user.photos.new(params[:photo])
if #product.valid? && #photo.valid?
#product.save
#photo.save
#photo.product_id = #product.id
render "show", :notice => "Sale created!"
else
render "new", :notice => "Somehting went wrong!"
end
end
new product page(HAML)
%h1
create item
= form_for #product,:url => products_path, :html => { :multipart => true } do |f|
%p
= f.label :name
= f.text_field :name
%p
= f.label :description
= f.text_field :description
%p
= fields_for :photo, :html => {:multipart => true} do |fp|
=fp.file_field :image
%p.button
= f.submit
schema.rb
create_table "products", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "user_id"
end
create_table "photos", :force => true do |t|
t.integer "product_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
end

This is merely a result of saving the #photo first, and then setting its product_id after saving, which of course never gets updated in the database. Just reverse the operations.
if #product.valid? && #photo.valid?
# Recommended to test for success saving the product
if #product.save
# Set the product_id before saving
#photo.product_id = #product.id
# Then save the photo
#photo.save
render "show", :notice => "Sale created!"
end
else
render "new", :notice => "Somehting went wrong!"
end

Related

Why I can't create article with any category?

I'm new in RoR, and I'm working on Blog app, and implementing categories for articles. But I have trouble - when I create any article with some categories ('sport' or 'movie' or any other) I receive validation errors
- Category must exist
- Category can't be blank
But I have working dropdown list or categories (this helper):
def categories
category =
["Sport",
"Movie",
"Art",
"Nature",
"Exotic"]
category.each do |categ|
my_category = "#{categ}"
end
return category
end
And here is a piece of code of my article.new.html.erb file:
<p>
<%= f.label :category %><br>
<%= f.select :category, categories,
prompt: "Choose your category" %>
</p>
Also here is my db schema where categories fields are present:
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "pic"
t.string "photo_file_name"
t.string "photo_content_type"
t.bigint "photo_file_size"
t.datetime "photo_updated_at"
t.string "music_file_name"
t.string "music_content_type"
t.bigint "music_file_size"
t.datetime "music_updated_at"
t.string "movie_file_name"
t.string "movie_content_type"
t.bigint "movie_file_size"
t.datetime "movie_updated_at"
t.string "category_id"
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.text "desc"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "comments", force: :cascade do |t|
t.string "commenter"
t.text "body"
t.bigint "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["article_id"], name: "index_comments_on_article_id"
end
create_table "subscribers", force: :cascade do |t|
t.string "f_name"
t.string "l_name"
t.string "email"
t.string "country"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "userid"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: false
end
add_foreign_key "comments", "articles"
end
And here is my models:
class Article < ApplicationRecord
belongs_to :category
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 3}
validates :text, presence: true, length: {minimum: 3}
validates :category_id, presence: true
end
class Category < ApplicationRecord
has_many :articles
end
Also this is my Article controller:
class ArticlesController < ApplicationController
before_action :admin_authorize, :except => [:index, :show, :search]
def index
#articles = Article.includes(:category).order("created_at DESC")
if params[:category].blank?
#articles = Article.all.order("created_at DESC")
else
#category_id = Category.find_by(name: params[:category]).id
#articles = Article.where(category_id: #category_id).order("created_at DESC")
end
end
def new
#article = Article.new
#categories = Category.all.map{|c| [c.name, c.id]}
end
def show
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
#article.category_id = params[:category_id]
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: "Article was successfully created!" }
format.json { render :show, status: :created, location: #article }
else
format.html { render :new}
format.json {render json: #article.errors, status: :unprocessable_entity}
end
end
end
def edit
#article = Article.find(params[:id])
#categories = Category.all.map{|c| [ c.name, c.id ] }
end
def search
if params[:search].blank?
#articles = Article.all
else
#articles = Article.search(params)
end
end
def update
#article = Article.find(params[:id])
#article.category_id = params[:category_id]
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :search, :music, :movie, :photo)
end
def find_article
#article = Article.find(params[:id])
end
end
Thanks in advance!!
You forgot the category_id in your permitted params:
def article_params
params.require(:article).permit(:title, :text, :search, :music, :movie, :photo, :category_id)
end
You also need to change your select helper to send category_id and not category in the POST request.
Now, with your categories helper, you don't send any category id in your select dropdown, just some "category" names which are not bound to any Category instances.
You can fix the select like this and remove your helper:
<p>
<%= f.label :category %><br>
<%= f.select :category_id, Category.all.collect{|c| [c.name, c.id]},
prompt: "Choose your category" %>
</p>

Connecting two models in many-to-many relationship

I have a problem.
I want to get all matches in my player#show where player was engaged. So I did a many-to-many relationship.
Match model:
class Match < ActiveRecord::Base
has_many :match_schedules
has_many :players, through: :match_schedules
end
Player model:
class Player < ActiveRecord::Base
has_many :match_schedules
has_many :matches, through: :match_schedules
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end
and my match_schedule model:
class MatchSchedule < ActiveRecord::Base
belongs_to :player
belongs_to :match
end
If i do something like this in rails console:
p = Player.find 1
m = p.matches.new
m.playerA = “leo”
m.playerB = “cris”
p.save
It works, i can display loop with name:
<% #player.matches.each do |match| %>
<%= match.playerA %>
<% end %>
Problem is that i don't really know how I can connect matches to player in my new form, in browser. Already i have something like this:
Players_helper:
module PlayersHelper
def player_hash(players)
hash = Hash.new
players.each do |player|
hash["#{player.first_name}" + " " + "#{player.last_name}"] = player.first_name + player.last_name
end
hash
end
end
and _form:
<div class="form-inputs">
<%= f.select :playerA, options_for_select(player_hash(#abc)) %>
<%= f.select :playerB, options_for_select(player_hash(#abc)) %>
<%= f.input :PlayerApoints %>
<%= f.input :PlayerBpoints %>
</div>
Matches controller for new and create method looks like:
def new
#match = Match.new
#abc = Player.all
end
def create
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render :show, status: :created, location: #match }
else
format.html { render :new }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
And my schema.rb:
ActiveRecord::Schema.define(version: 20150706185030) do
create_table "match_schedules", force: :cascade do |t|
t.integer "match_id"
t.integer "player_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "matches", force: :cascade do |t|
t.string "playerA"
t.string "playerB"
t.integer "PlayerApoints"
t.integer "PlayerBpoints"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "players", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
end
You left out your match_params hash, so I have to do some guessing but something along the lines of:
in your Match class:
class Match < ActiveRecord::Base
has_many :match_schedules
has_many :players, through: :match_schedules
accepts_nested_attributes_for :match_schedules
end
in your controller:
def new
#match = Match.new
#match.match_schedules.build
#abc = Player.all
end
def create
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render :show, status: :created, location: #match }
else
format.html { render :new }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
in your match_params whitelist you'll need to add:
..., :player_ids => [])
since it is an array you need to put it after the other params.
You will also have to modify your view code. Basically you want to return a match_shedules_attributes => {player_ids => [1,2]} this gives you the ability to tell the MatchSchedule table the ID of each player associated with that match_id. You can do this with a fields_for inside the form_for block. See this http://guides.rubyonrails.org/form_helpers.html
So in the create action in the Match controller it should also save two records in the MatchSchedule table, one with each player's id and the id of that match.

Active record toggle method

I would like to change value from false to true when users push a button once. And if users push the button again, value should change from true to false.
I tried with the code below but failed. How can I solve this?
☆show.html.erb(group_messages_controller)
<div class="bottom">
<%= link_to"解決!", {:controller =>'group_messages', :action => 'kaiketsu', :id=>#group_message.id}, class: "btn btn-medium btn-default" %></b>
</div>
☆group_messages_controller
def show
if !checklogin? then return end
#group_message = GroupMessage.find(params[:id])
#isme = me? #group_message
#group_message_comment = GroupMessageComment.new
#group_message_comment = GroupMessage.find(params[:id]).group_message_comments.order("created_at asc").build
#isme_cmt = me? #group_message_comment
#gmsc_member_id = session[:user_id]
#gm_in = GroupMessage.where(:group_id => #group_message.group_id).order("created_at desc").paginate(:page => params[:page], :per_page => 6)
#gm_in_one = #gm_in.map{|gm| gm}
respond_to do |format|
format.html # show.html.erb
format.json { render json: #group_message }
end
end
def kaiketsu
#group_message= GroupMessage.find(params[:id])
#group_message.toggle(:kaiketsu)
#group_message.update_attributes(params[:group_message])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #group_message }
end
end
☆schema.rb
create_table "group_messages", :force => true do |t|
t.integer "member_id", :null => false
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "group_id", :null => false
t.integer "page"
t.integer "line"
t.string "name"
t.string "username"
t.boolean "kaiketsu", :default => false
end
You should use toggle! method instead toggle. Because method without ! doesn't save the changes.

Creating multiple records with one form

I'm trying to get a nested form to work but keep getting the error below:
ActiveModel::MassAssignmentSecurity::Error in ResponsesController#create
Can't mass-assign protected attributes: gate_answer
Here are my models... what am I doing wrong?
class Response < ActiveRecord::Base
attr_accessible :gate_id, :gate_answers_attributes
belongs_to :gate
has_many :gate_answers
accepts_nested_attributes_for :gate_answers
end
class GateAnswer < ActiveRecord::Base
attr_accessible :prospect_id, :gate_question_id, :content, :response_id
belongs_to :response
end
And the DB schema extract:
create_table "gate_answers", :force => true do |t|
t.integer "prospect_id"
t.integer "gate_question_id"
t.text "content"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "response_id"
end
create_table "responses", :force => true do |t|
t.integer "gate_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
..and my controllers
# responses_controller.rb
def create
#response = Response.new(params[:response])
respond_to do |format|
if #response.save
format.html { redirect_to :action => 'index', :gate_id => (params[:response][:gate_id]), notice: 'Response was successfully created.' }
else
format.html { render action: "new" }
end
end
end
#gate_answers_controller.rb
def create
#gate_answer = GateAnswer.new(params[:gate_answer])
#code below finds URL that user will be redirected to after form is saved
#gate = Gate.find(params[:gate_answer][:gate_id])
respond_to do |format|
if #gate_answer.save
format.html { redirect_to #gate.content_url, notice: 'Redirecting.' } #redirect to URL per above
else
format.html { render action: "new" }
end
end
end
What am I doing wrong?
Try Form Object approach from here http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
A way of creating this type of form is shown in detail in the Railscasts Nested Model Form Part 1 & Part 2. Maybe this will help you?

Why does my form_for area#new have a button that says "Update Area", not "Create Area"

I have an area model that belongs to a report model. I have built a form partial using SimpleForm. When I go to new_report_area_path(#report), I get a New Area form that works just fine. Enter the details and hit submit and it creates an area and takes you to area#show. But the button on the new area form says "Update Area" not "Create Area". Why?
config/routes.rb:
Testivate::Application.routes.draw do
resources :reports do
resources :areas
end
end
db/schema.rb:
ActiveRecord::Schema.define(:version => 20121205045544) do
create_table "areas", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "report_id"
end
create_table "reports", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
app/models/area.rb:
class Area < ActiveRecord::Base
attr_accessible :name
has_many :heuristics
belongs_to :report
end
app/models/report.rb:
class Report < ActiveRecord::Base
attr_accessible :name
has_many :heuristics
has_many :areas
end
app/controllers/areas_controller.rb:
class AreasController < ApplicationController
filter_resource_access
def new
#report = Report.find(params[:report_id])
#area = #report.areas.create
respond_to do |format|
format.html # new.html.erb
end
end
def create
#report = Report.find(params[:report_id])
#area = #report.areas.create(params[:area])
respond_to do |format|
if #area.save
format.html { redirect_to report_area_path(#report, #area), notice: 'Area was successfully created.' }
else
format.html { render action: "new" }
end
end
end
end
app/views/areas/news.html.haml:
%h1 New Area
= render 'form'
app/views/areas/_form.html.haml:
= simple_form_for [#report, #area] do |f|
= f.error_notification
= f.input :name
= f.button :submit
Instead of creating an area you should building it as it's a new action:
def new
#report = Report.find(params[:report_id])
#area = #report.areas.build # build instead of create
respond_to do |format|
format.html # new.html.erb
end
end

Resources