I want to use the link_to update the is_completed boolean of the task
contact has many tasks.
resources :contacts do
resources :tasks
end
===============
create_table "tasks", force: :cascade do |t|
t.text "content"
t.date "due"
t.boolean "is_completed", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "contact_id"
end
========
how can do that in the contact/index
# in your view
<%= link_to 'make it complete', contact_task(#contact, #task), method: :patch %>
# in your controller
def update
contact = Contact.find(params[:id])
contact.update_attribute(:is_completed, true)
# ...
end
update_attribute has some restriction, alternatively:
contact.is_completed = true
contact.save
Related
I have two tables, accounts and items and I would like to show the buisness_name instead of the idfrom the accounts table on the view/items/show.html.erb page.
Currently I have no associations between the models, but I have the account_id column in the items table.
create_table "accounts", force: :cascade do |t|
t.string "buisness_name"
t.string "web_site"
t.string "phone_number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
t.json "attachments"
t.integer "account_id"
end
I'm getting the account_id via this: <%= #item.account_id %>, but I would like to show the buisness_name instead.
Try something like this
class Item < ActiveRecord::Base
belongs_to :account
end
Into the view
<% if #item.account %>
<%= #item.account.buisness_name %>
<% end %>
Should be business_name, as #Sergio Tulentsev already told you
I updated my answer because I noticed from the table that the account_id has not a not null constraint
If you have
<%= #item.account_id %>
The horrible way to get the account would be
<%= Account.find_by(id: #item.account_id).try(:buisness_name) %>
Much smarter would be
class Item
belongs_to :account
delegate :buisness_name, to: :account, prefix: true, allow_nil: true
And then in the view...
<%= #item.account_buisness_name %>
I'm trying to build an app for online single page quizzes, so i'm using nested forms. My problem comes when i try to access data from nested classes, I'm confused on how to handle this as a best practice.
The app has a Quiz class, which has Questions, and each question has multiple Alternatives.
quizzes_controller.rb :
def take
#quiz = Quiz.find(params[:id])
#user_quiz = #quiz
#SAVE ANSWERS TO CURRENT USER
end
take.html.erb :
<%= simple_form_for #user_quiz do |quiz| %>
<%= quiz.simple_fields_for :questions do |question| %>
**<!-- this should show question.statement -->**
<div class="custom-controls-stacked">
<%= question.input :answer, collection: **#this should be question.alternatives**
,as: :radio_buttons, class:"custom-control-input" %>
</div>
<% end %>
<%= quiz.button :submit %>
<% end %>
quiz.rb :
class Quiz < ApplicationRecord
belongs_to :session
has_many :questions
accepts_nested_attributes_for :questions
end
question.rb
class Question < ApplicationRecord
belongs_to :quiz
has_many :alternatives
end
alternative.rb
class Alternative < ApplicationRecord
belongs_to :question
end
schema.rb
ActiveRecord::Schema.define(version: 20171008213618) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "alternatives", force: :cascade do |t|
t.text "answer"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "question_id"
t.index ["question_id"], name: "index_alternatives_on_question_id"
end
create_table "questions", force: :cascade do |t|
t.text "statement"
t.integer "correct_answer"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quiz_id"
t.integer "answer", default: 0
t.index ["quiz_id"], name: "index_questions_on_quiz_id"
end
create_table "quizzes", force: :cascade do |t|
t.integer "minutes", default: 20
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "session_id"
t.index ["session_id"], name: "index_quizzes_on_session_id"
end
end
How can I access said data (question.statement is a string, and question has_many alternatives), when questions is a nested class?
I'd like to sum quantities by category in the different models (Shop and Item).
models
class Order < ActiveRecord::Base
has_many :shops
end
class Shop < ActiveRecord::Base
belongs_to :order
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :shop
has_one :order, autosave: false, through: :shop
end
There are the columns category and quantity in the both shops and items table as below.
I'd like to sum and display both quantity by category.
schema.rb
ActiveRecord::Schema.define(version: 20160610051929) do
create_table "orders", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shops", force: :cascade do |t|
t.string "name"
t.integer "order_id"
t.integer "category"
t.integer "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "name"
t.integer "category"
t.integer "quantity"
t.integer "shop_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
orders_controller.rb
class OrdersController < ApplicationController
def show
#orders = Order.find(params[:id])
end
end
view/orders/show.html.erb
<%= render #orders %>
My current view is as follows;
Although it only calculate in items table, I'd like to add the quantity in shops.
view/orders/ _order.html.erb
<% order.shops.each do |shop| %>
<% shop.items.group(:category).sum(:quantity).each do |category, sum| %>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
Although I also tried the following view instead of the above code, it only works when the shop's category exist in item's category.
If there is no category same as shop's category in the item, the result is not what I'd like to.
view/orders/ _order.html.erb
<% order.shops.each do |shop| %>
<% shop.items.group(:category).sum(:quantity).each do |category, sum| %>
<% if shop.category.present? && shop.quantity.present? && category == shop.category %>
<% sum = sum + shop.quantity %>
<% end %>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
It would be appreciated if you could give me how to sum and display both quantities or better way.
SOLVED
I could do what I'd like to do as followings;
schema.rb
create_table "categories", force: :cascade do |t|
t.integer "category"
t.integer "quantity"
t.integer "shop_id"
t.integer "item_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order_id"
end
create_table "items", force: :cascade do |t|
t.string "name"
t.integer "shop_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "shops", force: :cascade do |t|
t.string "name"
t.integer "order_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
orders_controller.rb
class OrdersController < ApplicationController
def show
#orders = Order.find(params[:id])
#categories = Category.where(order_id: params[:id])
end
end
_order.html.erb
<% order.shops.each do |shop| %>
<% #categories.where("order_id = ?", order.id).each do |cate| %>
<%= cate.order_id %>, <%= cate.shop_id%>, <%= cate.item_id%>, <%= cate.category%>, <%= cate.quantity%> <br>
<% end %>
<% #categories.where("shop_id = ?", shop.id).group(:category).sum(:quantity).each do |category, sum|%>
Category <%=category%> : <%= sum %><br>
<% end %>
<% end %>
Your second approach should work, style-wise you can write it much shorter:
sum += shop.quantity.to_i if shop.category == category
nil.to_i is 0, so no matter if shop.quantity is nil or not you can safely add it as long as shop.category == category And if shop.categoryis nil, it won't be equal to category, so there is no need to ask for it's presence either.
That said, I still think you might want to rethink the way your models are.
I am trying to make a simple blog app with rails as part of an exercise. Right now, you have the ability to add a comment on the post_path directly on show page of a post.
I'd like to be able to save a Comment object and have it be nested under a Post object so they are related.
My form looks like this:
#_form.html.erb
<%= form_for #comment, :url => post_comments_path(#post) do |f| %>
<%= f.label :content %>
<%= f.text_field :content %>
<%= f.submit %>
<% end %>
I click submit and I am transported to the comments controller:
#comments_controller.rb
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new
#comment.save(comment_params) # #comment is saved but :content is nil
if #comment.save(comment_params)
redirect_to post_path(#post)
end
end
if I look at comment_params inside the create action I see:
=> {"content"=>"asdfasdfasdfasdf"}. The comment gets saved, however the :content portion is empty as you can see below:
=> #<Comment:0x007fd1da63ce60
id: 4,
content: nil,
post_id: "1",
created_at: Mon, 26 Oct 2015 21:45:22 UTC +00:00,
updated_at: Mon, 26 Oct 2015 21:45:22 UTC +00:00,
user_id: nil>
I do have :content white listed in my strong params:
def comment_params
params.require(:comment).permit(:content, :post_id)
end
Here are my models..
#post.rb
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
has_many :comments
belongs_to :user
validates :title, :presence => true
validates :body, :presence => true
###methods###
def all_tags=(names)
self.tags = names.split(",").map do |name|
Tag.where(name: name.strip).first_or_create!
end
end
def all_tags
self.tags.map(&:name).join(", ")
end
end
#comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
end
and my schema.
# schema..
create_table "comments", force: :cascade do |t|
t.string "content"
t.string "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "posts_tags", force: :cascade do |t|
t.integer "post_id"
t.integer "tag_id"
end
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
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.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
Try changing to:
#comments_controller.rb
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.new(comment_params)
if #comment.save
redirect_to post_path(#post)
end
end
Update with explanation:
old:
# creates empty comment that belongs to #post
#comment = #post.comments.new
new:
#creates a comment that belongs to #post and has the content of comment_params
#comment = #post.comments.new(comment_params)
old:
#comment.save(comment_params)
if #comment.save(comment_params)
redirect_to post_path(#post)
end
# is the same as #comment.save({}) as far as save is concerned.
# save takes a hash for options. It only uses the options it knows
# So that is why it didn't complain. It didn't find anything it knew
# in the hash comment_params. So it just saved the empty comment
new:
if #comment.save # save the new comment that was generated with comment_params
redirect_to post_path(#post)
end
I have a the following form:
<h2>Add collaborators to the wiki <strong><%= #wiki.title %></strong></h2>
<%= form_for ([#wiki, #collaboration]) do |f| %>
<% #users.each do |user| %>
<p><%= check_box_tag 'user_ids[]', user.id %>
<%= label_tag 'user_ids[]', user.email %>
<% end %>
<p> <%= f.submit %> <p>
<% end %>
It should do the following, provide the possible the check users => an then all this users should be able to edit this particular form (#wiki)
I therefor created a join table which takes a user_id and wiki_id. If i try to
to save the collaborators in through the form it does not seem to work however.
I get this in my rails c
#<Collaboration id: 1, user_id: nil, wiki_id: 1, created_at: "2015-02-20 10:40:49", updated_at: "2015-02-20 10:40:49">,
So it does not seem to fetch the user.
My controller is set up like this
class CollaborationsController < ApplicationController
def new
#wiki = Wiki.find(params[:wiki_id])
#collaboration = #wiki.collaborations.new
#users = User.all
end
def create
#wiki = Wiki.find(params[:wiki_id])
#selected users
#collaboration = #wiki.collaborations.build(user_id: params[:user_id])
if #collaboration.save
redirect_to wikis_path, notice: "Wiki shared."
else
flash[:error] = "Error creating wiki. Try again."
render :new
end
end
end
And my schema file looks like this:
create_table "collaborations", force: :cascade do |t|
t.integer "user_id"
t.integer "wiki_id"
t.datetime "created_at"
t.datetime "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"
t.string "role"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
create_table "wikis", force: :cascade do |t|
t.string "title"
t.text "body"
t.boolean "private"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "wikis", ["user_id"], name: "index_wikis_on_user_id"
create_table "wikis_and_collaborators", force: :cascade do |t|
t.integer "user_id"
t.integer "wiki_id"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Any thoughts on what goes wrong here?
Let's say we have params[:user_ids] = [123, 456, 789]
You can say #wiki.user_ids = [123, 456, 789]; #wiki.save and that will make the join records automatically. So, this is actually an update on the wiki object, and your form should be editing the Wiki object too. I would do it like so:
<h2>Add collaborators to the wiki <strong><%= #wiki.title %></strong></h2>
<%= form_for (#wiki) do |f| %>
<% #users.each do |user| %>
<p><%= check_box_tag 'wiki[user_ids][]', user.id, #wiki.user_ids.include?(user.id) %>
<%= label_tag 'wiki[user_ids][]', user.email %>
<% end %>
<p> <%= f.submit %> <p>
<% end %>
This will submit to the WikiController#update action, or the WikiController#create action, depending on whether #wiki is a new record or not.
params will be params = {:id => 6, :wiki => {:user_ids => [123, 456, 789]}}, where 6 is an example wiki id.
Following convention, you wouldn't be accessing the CollaborationsController at all, you'd be accessing the WikisController, since it's a Wiki that's being updated. The WikisController#update action would be totally standard:
def update
#wiki = Wiki.find_by_id(params[:id])
#wiki.update_attributes(params[:wiki])
redirect_to wiki_path(#wiki) #or whatever
end