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 %>
Related
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'm a newbie and struggling a little with this:
I have two models: User & Job, the relationship is as follows:
class User < ApplicationRecord
has_many :jobs, dependent: :destroy
end
class Job < ApplicationRecord
belongs_to :user
end
In my jobs index view I have a search form where I want to locate jobs by address (ie: look for the User's address),
<%= form_tag(jobs_path, method: :get) do %>
<%= text_field_tag :address, params[:address] %>
<%= submit_tag 'Search', class:'btn btn-primary' %>
<% end %>
which is one of the user's attribute:
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.float "latitude"
t.float "longitude"
***t.string "address"***
t.string "phone"
t.index ["email"], name: "index_users_on_email", unique: true
end
In my JobsController, how do I point to the User's attributes? ie: User's address ? This is the index function I have for now:
def index
#jobs = if params[:address]
Job.where('address LIKE ?', "%#{params[:address]}%").paginate(page: params[:page], per_page: 20)
else
#jobs = Job.paginate(:page => params[:page], per_page: 4)
end
end
But obviously I'm not getting any thing when doing a search.
Thank you for your guiding advice in advance. Rodolphe
You should do something like:
Job.joins(:user).where(users: { address: address }) }
Amended the code base in 'contacts controller' from #contacts = Contact.all to #contacts = current_user.contact so a user can see only their contacts (currently any user sees all the contacts) This is throwing the below error. Tried tweaking but still no success and have checked the database in psql and both have id column. Any ideas or amendment needed in the code?
Failure/Error: <% if #contacts.any? %> ActionView::Template::Error: PG::UndefinedColumn: ERROR: column contacts.user_id does not exist LINE 1: SELECT 1 AS one FROM "contacts" WHERE "contacts"."user_id" ... ^ : SELECT 1 AS one FROM "contacts" WHERE "contacts"."user_id" = $1 LIMIT 1
Contacts Controller
class ContactsController < ApplicationController
before_action :contact, only: [:show, :edit, :update, :destroy] before_action :authenticate_user!
def index
#contacts = current_user.contact end
def new
#contact = Contact.new end
def create
Contact.create(contact_params)
redirect_to '/contacts' end
def show end
def edit end
def update
#contact.update(contact_params)
redirect_to '/contacts/' + "#{#contact[:id]}" end
def destroy
#contact.destroy
redirect_to '/contacts' end
private
def contact_params
params.require(:contact).permit(:firstname, :surname, :email, :phone, :image) end
def contact
#contact = Contact.find(params[:id]) end
end
User controller
class UsersController < ApplicationController
end
Contact model
class Contact < ActiveRecord::Base
belongs_to :user
has_attached_file :image, styles: {thumb: "100x100>"}
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
User model
class User < ActiveRecord::Base
has_many :contacts, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Index html
<%if user_signed_in? %>
<%= link_to 'Log out', destroy_user_session_path, method: :delete %>
<%end%>
<% if #contacts.any? %>
<% #contacts.each do |contact| %>
<%= link_to image_tag(contact.image.url(:thumb)), contact_path(contact) %>
<h3><%= contact.firstname%> <%=contact.surname%></h3>
<%=contact.email%><br />
<%=contact.phone%>
<br />
<br />
<%end%>
<%else%>
No contacts yet!
<%end%>
<br />
<br />
<%= link_to 'Add a contact', new_contact_path%>
Schema
ActiveRecord::Schema.define(version: 20160504125849) 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"
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
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
Basically you do not have the user_id in the contacts table. That is the foreign key which is used to define a relation to the users. Add the column and then when you create the contact, add the user's id to the user_id column in the contacts table. Then that should work.
I am using Simple Form and the Invoicing gem, but when I try to render the form, I get "RuntimeError in InvoicingLedgerItems#new" with "Association :sender_id not found". I want to save the primary key of the Users table (Devise gem) in the sender_id field (because the user is the sender of the invoice). I tried using the foreign_key option in the model, but it didn't seem to have any effect. Here's my code without using the foreign_key option.
Models:
class InvoicingLedgerItem < ActiveRecord::Base
acts_as_ledger_item
belongs_to :user
has_many :line_items, class_name: 'InvoicingLineItem', foreign_key: :ledger_item_id
accepts_nested_attributes_for :line_items
end
class User < ActiveRecord::Base
has_many :invoicing_ledger_items
end
View:
<%= simple_form_for #invoicing_ledger_item do |f| %>
<%= f.association :sender_id %>
<%= f.button :submit %>
<% end %>
Schema:
create_table "users", force: true 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.datetime "deleted_at"
end
create_table "invoicing_ledger_items", force: true do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.string "type"
t.datetime "issue_date"
t.string "currency", limit: 3, null: false
t.decimal "total_amount", precision: 20, scale: 4
t.decimal "tax_amount", precision: 20, scale: 4
t.string "status", limit: 20
t.string "identifier", limit: 50
t.string "description"
t.datetime "period_start"
t.datetime "period_end"
t.string "uuid", limit: 40
t.datetime "due_date"
t.datetime "created_at"
t.datetime "updated_at"
end
You Need to Make a relation with user model through foreign key 'sender_id'
Model
class InvoicingLedgerItem < ActiveRecord::Base
acts_as_ledger_item
belongs_to :user, foreign_key: :sender_id
has_many :line_items, class_name: 'InvoicingLineItem', foreign_key: :ledger_item_id
accepts_nested_attributes_for :line_items
end
Need to make association with user as hidden field
View:
<%= simple_form_for #invoicing_ledger_item do |f| %>
<%= f.association :user, :as => :hidden, :input_html => { :value => curren_user.id } %>
<%= f.button :submit %>
<% end %>
You should use user_id instead of sender_id in the schema first.Then use alias_attribute :user_id, :sender_id in InvoicingLedgerItem model, through this helper method you can assign a new name to the field name.
For more reference visit http://api.rubyonrails.org/classes/Module.html.
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.