Im very new tor ails and still learning. My project that I am working on is set up for a user to have many cars and cars belong to a user.
class User < ApplicationRecord
validates :name, presence: true, uniqueness: true
has_secure_password
has_many :cars
end
class Car < ApplicationRecord
belongs_to :user
end
My flow is set up to where after a user signs in they can create their vehicle. However I am having trouble creating the car so that it will attach to the user and I am getting an error each time I try different methods.
def create
#car = Car.new(car_params)
#user = User.find(params[:id])
#car.save
redirect_to user_path
end
private
def car_params
params.require(:car).permit(:user_id, :make, :model, :color)
end
my schema seems to be set up correctly
create_table "cars", force: :cascade do |t|
t.string "make"
t.string "model"
t.string "color"
t.integer "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_cars_on_user_id"
end
But again, I am clearly doing something wrong and I am having trouble finding my answers online.
Well, if you have a login, you probably have a current_user set up. The logged one. Inside your create method on your controller you can set the car information using the logged in user like:
def create
#car = Car.new(car_params)
#car.user_id = current_user.id
#car.save
redirect_to user_path
end
You can also remove the :user_id from the car_params if you have the information about the logged user.
If you don't have the current_user or whatever method to get the logged in user created, you will need to select the user inside the form via a HTML select or something like that, then you can keep the :user_id inside car_params and just remove the line about setting the user_id on #car to save.
Related
I'm new to RoR and I want to create simple page like a task manager (to add and remove tasks) so I created 2 tables with association between them (Track and Item).
Here is 2 models:
class Item < ApplicationRecord
belongs_to :track, optional: :true
end
class Track < ApplicationRecord
has_many :items, dependent: :destroy
end
And I need to set association when I create or delete any track item. But when I create it I just see my track item (with an empty field in associated table)
For example:
rails c
Track.create(item: 'Asafa Pauel', description: 'This is a description') - works fine (added all field to db)
Item.all - track_id field is empty - but it should show id of track item. Why is this?
And my Tracks controller:
class TracksController < ApplicationController
def index
#track = Track.all
end
def show
#track = Track.all
end
def new
#track = Track.new
end
def create
#track = Track.new(track_params)
#item = Item.new(track_id: #track.id)
if #track.save! && #item.save!
flash[:success] = "It works!"
redirect_to tracks_path
else
flash[:success] = "Its wrong!"
end
end
private
def track_params
params.require(:track).permit(:item, :description)
end
end
And Items controller:
class ItemsController < ApplicationController
def create
#item = Item.new(item_params)
end
private
def item_params
params.require(:item).permit(:track_id)
end
end
And db schema:
ActiveRecord::Schema.define(version: 2019_05_23_112947) do
enable_extension "plpgsql"
create_table "items", force: :cascade do |t|
t.bigint "track_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["track_id"], name: "index_items_on_track_id"
end
create_table "tracks", force: :cascade do |t|
t.string "item"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Thanks in advance
Your new 'Track' object doesn't have an ID yet, so you can't assign its value to Item.track_id.
First you'll have to save the Track, then create a new Item.
Also, if you create a new Track from console, you won't trigger your "create" method in the controller: it will be called only if you create a new Track from browser.
If you want to create a new Item every time you create a Track, you'll have to do something like this in your model file "track.rb":
after_save :create_new_item
def create_new_item
self.items.create
end
P.S.: the "track.rb" file is in "app/models" in your Rails application.
My rails_admin application is having two roles namely Teacher and Student such that every user belongs_to a role. I am using the gem cancancan to manage roles.
app/models
class Role < ApplicationRecord
has_many :users
end
class User < ApplicationRecord
belongs_to :role
has_many :projects
end
class Project < ApplicationRecord
belongs_to :user
end
Project schema
create_table "projects", force: :cascade do |t|
t.string "name"
t.string "description"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_projects_on_user_id", using: :btree
end
Now, I want the project list to show all the data when role is Teacher but it should only show those projects where project.user_id == user.id when the role is Student.
That is, the final aim is to allow the role Student to see only his/her own projects thereby restricting the role Student to see all the projects where as the role Teacher should be able to see all the projects.
To get data based on current user role I made the following changes to models/ability.rb.
Under the case when user role is Student
can :read, Project, ["user_id = ?", user.id] do |project|
project.present?
end
It will compare the user_id in projects table with the user.id so, in case of role Student it will only fetch the projects created by that student.
Your controller will have an action to list projects.
Just use a before_action filter to load projects based on role.
Assuming, you are setting current_user in application's base controller and have association b/w student and project - student has_many projects.
ProjectController
before_action :load_projects, only: [:index]
def index
#projects
end
private
def load_projects
if current_user.role == "Teacher"
#projects = Project.all
elsif current_user.role == "Student"
#projects = current_user.projects
end
end
end
You can also add association between projects and teacher. And user teacher.projects if you need.
There is also another approach to achieve the functionality
create a new column called role in users table
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", null: false
t.datetime "updated_at", null: false
t.integer "role"
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
And in the user model
class User < ApplicationRecord
enum role: [:student, :teacher, ]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :student
end
end
This creates a default role as student when a new user signs up.you can change the role of the user to teacher by simply doing
go to rails console in the terminal using
rails c
u = User.find_by_id(1)
# you can change value '1' depending upon your user_id
u.role = "teacher"
u.save
Now the role of the user is teacher.
class ProjectsController < ApplicationController
if current_user.teacher?
#projects = Project.all
else
#project = current_user.projects
end
class User < ApplicationRecord
belongs_to :role
has_many :projects
def projects
if self.role.to_s == "student"
super projects
else
Project.all
end
end
end
The answers provided asume the controllers can be changed and as the questioner points he does not have access to them.
Not sure if cancancan will handle the filtering of records based on view(read) permission on the index views of rails admin with a rule like this:
can :read, Project, user_id: user.id
But on the edit view i know you can configure a has many field to scope it based on the current user like this:
edit do
field :projects do
associated_collection_scope do
current_user = bindings[:controller].current_user
proc { |scope| scope.where(user: current_user) }
end
end
end
I'm trying to create notifications in rails 5 with action cable. Wondering if anyone could help with my troubles .
Currently I have my notifications table
Schema
create_table "notifications", force: :cascade do |t|
t.string "activity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "recipient_id"
t.string "action"
t.string "notifiable_type"
t.integer "notifiable_id"
t.index ["user_id"], name: "index_notifications_on_user_id"
end
My Notification Model
class Notification < ApplicationRecord
belongs_to :user
belongs_to :recipient, class_name: "User"
belongs_to :notifiable, polymorphic: true
after_create_commit { NotificationBroadcastJob.perform_later(Notification.count,self)}
validates :user_id, presence: true
end
I'm having trouble understanding how to represent the user notifications within a controller and views.
For example I have the notification create method when a specific action is created.
class Comment < ApplicationRecord
after_create_commit { create_notification }
private
def create_notification
Notification.create action: "Your comment has been created", user_id: comment.user, recipient_id: comment.user
end
end
Here I attempt to tie the User & Notifications together within a helper method in application controller.
helper_method :current_notifications
def current_notifications
if current_user
#notifications = current_user.notifications.all
else
end
end
The error I receive is
SQLite3::SQLException: no such column: notifications.recipient_type: SELECT COUNT(*) FROM "notifications" WHERE "notifications"."recipient_id" = ? AND "notifications"."recipient_type" = ?
My problem again lies within how to represent the user's notifications. I'm pretty sure I'm confused on how to tie things together. I was attempting to follow Chris Oliver tutorial which I listed below.
https://gist.github.com/excid3/4ca7cbead79f06365424b98fa7f8ecf6
Any help or corrections would be helpful
In Comment:
def create_notification
Notification.create action: "Your comment has been created", user_id: comment.user.id, recipient_id: comment.user.id
end
I'm wondering if the error is coming from passing the entire user object to user_id and recipient_id rather than the id itself?
A little more critical thinking allowed me to answer my issue at least so far.
Within the application controller I ended up using
def current_notifications
if current_user
#notifications = Notification.all.where(user_id: [current_user.id])
else
end
end
So far that has solved the error I have been recieving
I have a user model and a shout model. I am trying to have a user be associated with a shout. I did not make this association upon creation of the shouts table so I had to run a new migration. Below is my table, the models of each, and the output when from my console I run a command to try and find the user_id of a shout. Can you see what I am doing wrong?
schema:
create_table "shouts", force: :cascade do |t|
t.string "title"
t.text "description"
t.integer "user_id"
end
add_index "shouts", ["user_id"], name: "index_shouts_on_user_id"
create_table "users", force: :cascade do |t|
t.string "email", null: false
t.string "password_digest", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
end
User Model:
class User < ActiveRecord::Base
validates :email, presence: true, uniqueness: true
validates :password_digest, presence: true
has_many :shouts
end
Shout Model:
class Shout < ActiveRecord::Base
belongs_to :user
end
Shout Controller:
class ShoutsController < ApplicationController
def new
#new_shout = Shout.new
end
def create
#new_shout = Shout.new(shouts_params)
if #new_shout.user_id == nil
render new_shout_path
elsif #new_shout.save
redirect_to dashboard_path
else
render new_shout_path
end
end
private
def shouts_params
params.require(:shout).permit(:title, :description, :user_id)
end
end
Some test code:
> Shout.find(4)
> #<Shout id: 4, title: "four", description: "four", user_id: nil>
Creating an instance of user from the console, working:
> User.first.shouts.create(title: 'four', description: 'four')
>[["title", "four"], ["description", "four"], ["user_id", 1]
Migration file:
class AddUserRefToShouts < ActiveRecord::Migration
def change
add_reference :shouts, :user, index: true, foreign_key: true
end
end
Here are a couple admittedly hacky options (but they'll work) if you don't want to follow the approach suggested in the comments. You could pass the user_id as a hidden field so it'll be included in the params or you can expressly set it in the create action.
If you want to pass as a hidden field, on your shout form add:
<%= f.hidden_field :user_id, value: current_user.id %>
Alternatively, if you want to handle in your create action:
def create
#shout = Shout.new(shout_params)
#shout.user_id = current_user.id
#shout.save
end
I am currently working on a scoring system for my game application and for some reason points are not adding up. The goal is whenever the player guesses the correct answer, points are added. For each new player, the score is set to 0.
question#validate_answer:
def validate_answer
#correct_answer = Answer.where(correct: true).map(&:text)
#selected_answer = params[:answer]
#player_score = Player.where(id: params[:id]).map(&:score)
if #correct_answer.include? (#selected_answer)
#player_score[0] += 1
render :success
else
render :error
end
end
Quesiton.rb
class Question < ActiveRecord::Base
belongs_to :category
has_many :answers
has_one :video_clue
has_many :answers
def correct_answer
answers.find_by correct: true
end
end
Answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
Player.rb
class Player < ActiveRecord::Base
has_secure_password
def admin?
self.admin == 'admin'
end
end
Schema Tables for Answers and Players
create_table "answers", force: :cascade do |t|
t.integer "question_id"
t.string "text"
t.boolean "correct"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "answers", ["question_id"], name: "index_answers_on_question_id", using: :btree
create_table "players", force: :cascade do |t|
t.string "user_name"
t.string "password_digest"
t.integer "score"
t.string "role"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I would restructure the code the like this:
def validate_answer
#question = Question.find(params[:q_id])
#correct_answer = Answer.find_by_question_id(params[:q_id]).text.downcase
#selected_answer = params[:answer].downcase
#player = Player.find(params[:id])
if #selected_answer == #correct_answer
#player.increment!(:score)
render :success
else
render :error
end
end
increment! automatically pings the #player object for the column specified. It saved the trouble of trying to convert it to an array and access the index which was sort of roundabout.
NOTE: Edited Answer per chat conversation.
just see how less lines of code are need to be done for that
def validate_answer
#i think this is a bad approach to compare the text of the answer
#with the text of the given answer
#the better one would be to assing UUID to the answer or just use the regular ID
# something like this params[:answer][:uuid]
# or params[:answer][:id]
# we need to find the answer related to the question, otherwise we
# could throw in just random IDs and they are still saying "correct"
given_answer = Questions.find(params[:question_id]).answers.find params[:answer][:id]
# if we use UUID we dont need to give the question id, since then the ID can't be guessed
if given_answer.correct?
# current_user is a player.
# current_user is a method from devise (u should use devise)
current_user.award_points! given_answer
render :success
else
ender :error
end
end
class Player
def award_points! answer
# adding the points to the user
# and save it
current_user.award_points += answer.points
current_user.save
#better approach would be to save the question which was answered for the user
# like has_many :answered_questions
# so then u could also track which questions already been answered by the user and not awarding him twice
end
end