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
Related
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
Have a recipient and sender, both of the same class(Message) for a messaging system in rails. Want to set the params for both i.e. if user creates a message sender by default is the user_id and recipient will be the contact selected from the users contact list.
Currently the database is only receiving a user_id to the recipient_id column which is wrong and should be to sender_id column. Sender_id receives nothing.
After reading, some say not to amend the params as this is bad practice. So set a hidden field in the message view (like the body and title) yet this isn't pushing in to the database.
Two questions, is this process an appropriate rails practice? (ask this as new to rails) If not: can you advise another path or direction? If so: any ideas/thoughts why this isn't saving in to the database?
user model
class User < ActiveRecord::Base
has_many :messages, class_name: "Message", foreign_key: "recipient_id"
has_many :sent_messages, class_name: "Message", foreign_key: "sender_id"
has_many :contacts, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :firstname, allow_blank: false
validates_presence_of :surname, allow_blank: false
end
message model
class Message < ActiveRecord::Base
belongs_to :sender, class_name: "User", foreign_key: "sender_id"
belongs_to :recipient, class_name: "User", foreign_key: "recipient_id"
validates_presence_of :body, :title
end
Messages controller
class MessagesController < ApplicationController
before_action :message, only: [:show]
before_action :authenticate_user!
def index
#messages = current_user.messages
end
def new
#message = Message.new
end
def create
current_user.messages.create(message_params)
redirect_to '/messages'
end
def show
end
private
def message_params
params.require(:message).permit(:title, :body, :sender_id, :recipient_id)
end
def message
#message = Message.find(params[:id])
end
end
message/new view
<%= form_for #message do |f| %>
<%= hidden_field_tag :sender_id, current_user.id %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
schema
ActiveRecord::Schema.define(version: 20160517131719) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "contacts", force: :cascade do |t|
t.string "firstname"
t.string "surname"
t.string "email"
t.integer "phone"
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"
t.datetime "image_updated_at"
t.integer "user_id"
end
add_index "contacts", ["user_id"], name: "index_contacts_on_user_id", using: :btree
create_table "messages", force: :cascade do |t|
t.string "title"
t.text "body"
t.integer "sender_id"
t.integer "recipient_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.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 "firstname"
t.string "surname"
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
add_foreign_key "contacts", "users"
end
Try changing your form to this:
<%= form_for #message do |f| %>
<%= f.hidden_field :sender_id, value: current_user.id %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Currently the database is only receiving a user_id to the recipient_id
column which is wrong and should be to sender_id column.
In your create action, you have current_user.messages.create(message_params). This creates a message record in the DB with the foreign key's(i.e, recipient_id in your case) value with the parent's(user) id. This is the reason, the recipient_id gets the value of user's id.
Sender_id receives nothing.
This is because the hidden_field set for sender_id is not wrapped with the form builder instance. You need to change
<%= hidden_field_tag :sender_id, current_user.id %>
to
<%= f.hidden_field :sender_id, current_user.id %>
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
I have 3 tables- OwnerofProperty , Property and Ticket. I want to make a form using form_for to represent property booking; can I make a form to retrieve data from Property where the submit button saves the data in the Ticket table? I am asking because I have no idea if that can be possible or how to make it.
Note: I have only created the relations :
OwnerofProperty one-to-many Property
Property one-to-one Ticket
I need this form just to make a user able to see the avaliable properties and can book only one , how to make this form ?
Schema.rb for the three models :
create_table "owners", :force => true do |t|
t.string "f_name"
t.string "l_name"
t.string "address"
t.string "tel_no"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "properties", :force => true do |t|
t.string "p_street"
t.string "p_city"
t.string "postcode"
t.string "property_type"
t.integer "rooms"
t.integer "rent"
t.integer "owner_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "properties", ["owner_id"], :name => "index_properties_on_owner_id"
create_table "tickets", :force => true do |t|
t.string "city"
t.string "street"
t.string "ticket_type"
t.integer "rooms"
t.integer "rent"
t.integer "property_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "tickets", ["property_id"], :name => "index_tickets_on_property_id"
Yes, it is possible.
let's see ticket_controller.rb
def new
#property = Property.find 20 #20 is property id
#properties = Property.all
##ticket = Ticket.new
end
now in view (where you want to create form):
<%= form_for #ticket do |f| %>
<%= f.select :property_id, #properties.collect {|p| [ p.name, p.id ] }%> <!-- just an example, Ticket model has a field named "property_id" -->
<%= f.submit %>
<%= end %>
this form submits to create action of ticket_controller. And you are able to get all data as params and save it to table.
def create
#ticket = Ticket.new(params[:ticket])
#ticket.save
respond_to do |format|
format.html{redirect_to( your_desired_path)}
end
end
I keep getting this exception: "SQLite3::SQLException: no such column: books.user_id: SELECT "books".* FROM "books" WHERE ("books".user_id = 4)". Which sounds like there is no user_id in the books table.
So I just installed the Foreigner plugin and added "t.integer :user_id, :null => false" and "add_foreign_key(:books, :users)" in the book migration file. I ran "rake db:migrate", but still it is giving me the same exception.
I am using Rails 3 in Windows and Devise to authenticate user.
HOME VIEW
<p><%= link_to "Add new Book",:controller =>"book", :action => 'new' %></p>
<% #books.each do |b| %>
<p><%= b.author%></p>
<p><%= b.title%></p>
<%end%>
HOME CONTROLLER
class HomeController < ApplicationController
def index
#user = current_user
#user.books||=Book.new
#books=#user.books
end
end
BOOK CONTROLLER
class BookController < ApplicationController
def new
#books = Book.new
# redirect_to :controller=>"home" ,:action=>"index"
end
def create
#books = Book.new(params[:book])
if #books.save
render "home/index"
#redirect_to :controller=>"home" ,:action=>"index"
else
render :action => 'new'
end
end
CREATE TABLE/BOOK MIGRATION
class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.text :title
t.text :author
t.integer :user_id, :null => false
t.timestamps
end
add_foreign_key(:books, :users)
end
BOOK VIEW
<h1>Book#new</h1>
<%= form_for(:book) do |f| %>
<p><%= f.text_field :title %></p>
<p><%= f.text_field :author %></p>
<p><%= f.submit "Add book"%>
BOOK MODEL
class Book < ActiveRecord::Base
belongs_to :user
end
USER MODEL
class User < ActiveRecord::Base
has_many :books
# Include default devise modules. Others available are:
# :token_authenticatable, :lockable, :timeoutable and :activatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation,:firstname,:lastname,:school,:major,:sex,:zipcode
end
ROUTE
Campus::Application.routes.draw do
get "book/index"
get "book/edit"
get "book/new"
get "home/edit"
devise_for :users
resources :book
root :to=> "home#index"
match '/book/new' =>"home#index"
end
DATABASE SCHEMA
ActiveRecord::Schema.define(:version => 20110609055608) do
create_table "books", :force => true do |t|
t.text "title"
t.text "author"
t.integer "user_id", :null => false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "courses", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "strong_ins", :force => true do |t|
t.string "subject"
t.string "topic"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :limit => 128, :default => "", :null => false
t.string "password_salt", :default => "", :null => false
t.string "reset_password_token"
t.string "remember_token"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
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 "firstname"
t.string "lastname"
t.text "school"
t.text "major"
t.string "sex"
t.integer "zipcode"
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 "weak_ins", :force => true do |t|
t.string "subject"
t.string "topic"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
end
The user_id column should appear in the schema after running the migration. It's not in your listing, so I'd say that's the problem. Make sure rake db:migrate is completing without errors. You can redo the migration with rake db:rollback && rake db:migrate, if necessary.