I currently have a view page named "users". In my view page, I'm displaying a table with attributes pulled from the users table such as the first name, last name, last login, admin, etc... In the admin field, it displays from the users table true or false.
What I would like to accomplish is to create a drop down, that displays the user's current value for admin (true or false), and then be able to change this attribute from the view page. Currently, I'm having to use the console on the server to manually change a user's value to true or false to grant or revoke admin privileges. Needless to say, this is inconvenient. I'm still fairly new to Rails and have been using scaffolds up to this point. I'm not sure how to accomplish this, and any advise would be greatly appreciated!
Users 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.boolean "admin", default: false
t.string "first_name"
t.string "last_name"
end
Admin controller:
class AdminController < ApplicationController
before_action :authenticate_user!
def users
#users = User.all.order('last_name ASC')
end
def reports
end
end
Users view page:
<table class="table table-hover table-condensed">
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Email</th>
<th>Creation Date</th>
<th>Last Sign In</th>
<th>IP Address</th>
<th>Admin</th>
</tr>
</thead>
<tbody>
<% #users.each do |user| %>
<tr>
<td><%= user.last_name %></td>
<td><%= user.first_name %></td>
<td><%= user.email %></td>
<td><%= user.created_at.strftime("%-m/%-d/%y") %></td>
<td><%= user.current_sign_in_at.strftime("%-m/%-d/%y") %></td>
<td><%= user.current_sign_in_ip %></td>
<td><%= user.admin %></td>
</tr>
<% end %>
</tbody>
</table>
You could accomplish this in a couple of different ways. For example, you could let the link in the drop-down trigger a method toggle_admin for removing admin if user is admin or the reverse if that's the case.
First create the route, in routes.rb:
resource :users do
get :toggle_admin
end
in your view:
<%=link_to (#current_user.admin ? 'Remove admin' : 'Make admin'), toggle_admin_users_path %>
And in your controller, you could have something like this:
def toggle_admin
#current_user.update_attribute :admin, (#current_user.admin ? false : true)
end
Hmm... yeah, I don't know. Could work. As you can see, I assume here that you have a #current_user variable for picking up the relevant user, otherwise send in the user.id into these methods, either from the url params or directly into the controller method.
First you'll need a route and a corresponding controller action that handles the update.
In a RESTful way this would be in routes.rb: resources :users, only: [:update]
In your view you need a from, that sends the data to the controller:
<%= form_for user do %>
...
Each of the users in the #users.each should have it's own form and a submit button. To create the select (that's the dropdown with true/false value) use the Rails form helper.
The controller action could look something like this:
def update
user = User.find(params[:id])
user.update_attributes(user_params)
end
private
def user_params
params.require(:user).permit(:admin)
end
Related
hello I have 2 models User.rb and Guest.rb.
in my app the user is responsible for entering the Guest information. I have a table in my views that will show all guests. I would like each guest row to display the user who has entered their information. Iam having some trouble properly setting up the current_user method in my controller methods. Currently iam grabbing the current_usera nd entering it next to every guest. Thank you so much in advance.
Controller:
def new
#guest = Guest.new
end
def create
#guest = Guest.new(guest_params)
if #guest.save
redirect_to guests_path
else
render 'new'
end
end
def index
#guests = Guest.all
#user = current_user
end
def show
#guest = Guest.find(params[:id])
#user = current_user
end
def edit
#guest = Guest.find(params[:id])
end
def update
#guest = Guest.find(params[:id])
if #guest.update(guest_params)
flash[:success] = "Profile updated"
redirect_to #guest
else
render 'edit'
end
end
def destroy
Guest.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to guests_url
end
def guest_params
params.require(:guest).permit(:experience,:interaction,:mood,:guest_name,:room_num,:arrival_date,:departure_date,:opportunity_string,:employee,:notes,:opportunity)
end
end
Models:
has_and_belongs_to_many :users
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_and_belongs_to_many :guests
end
Views:
body{background-color:white;}
</style>
<h1 class="text-center mt-3">Guests</h1>
<div class="container-fluid" style="overflow-x: auto; mb-3">
<table class="table table-bordered text-center ">
<thead>
<tr style="background-color:#CFD2CF;font-size:1.4vw">
<th>GUEST</th>
<th>EXPERIENCE</th>
<th>ROOM</th>
<th>ARRIVAL</th>
<th>DEPARTURE</th>
<th>OPPORTUNITY</th>
<th>EMPLOYEE</th>
<th>DEPARTMENT</th>
</tr>
</thead>
<tbody>
<% #guests.each do |guest| %>
<tr style="background-color:<%=guest.mood%>">
<td> <%= link_to guest.guest_name, "/guests/#{guest.id}" %></td>
<td><%= guest.experience %></td>
<td><%= guest.room_num %></td>
<td><%= guest.arrival_date %></td>
<td><%= guest.departure_date %></td>
<td ><%= #user.current_user%></td>
<td><%= %></td>
<td><%= guest.interaction %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
Schema:
create_table "guests", force: :cascade do |t|
t.string "experience"
t.string "interaction"
t.string "mood"
t.string "guest_name"
t.string "room_num"
t.string "arrival_date"
t.string "departure_date"
t.string "opportunity_string"
t.string "employee"
t.string "notes"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id", null: false
t.index ["user_id"], name: "index_guests_on_user_id"
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.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "superadmin_role", default: false
t.boolean "supervisor_role", default: false
t.boolean "user_role", default: true
t.string "name"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "guests", "users"
end
A few things that will help:
Devise and current_user:
Devise takes care of current_user for you, so you should just be able to call current_user in your controllers and views.
#user.current_user isn't a method unless you've created one in your User model, which I would not recommend doing.
Don't mix #user with current_user
It's possible to set #user = current_user, but I think it's a bad practice as it will get confusing quickly
#user should be tied to the model User and represent the user that the current_user is interacting with.
For example, a URL like /users/1/edit should set #user = User.find(1).
The current_user could be someone else who is editing the #user object.
HABTM Associations
Given an assigned #user, you can call #user.guests to get all guests associated with that user.
E.g. for a route that creates the following URL: /users/1/guests then your controller can have something like this:
# users_controller.rb
class UsersController < ApplicationController
...
def guests
#user = User.find(params[:id])
#guests = #user.guests
end
end
And the reverse is true as well. For a route like guests/1/users you can call #guest.users.
But...
Do you really want a HABTM?
If a User can create many Guests, but a Guest is never associated with many Users, then this isn't really a "has and belongs to many" relationship. You probably just want a simple has_many belongs_to.
I revisit this article often as a refresher when I'm considering my relationships.
Your schema has a user_id on your guest model, which indicates to me that you want to be able to say: "A User has_many guests. A Guest belongs_to a user."
But you wouldn't really say "A Guest can belong_to many Users"
Code fixes
I have a table in my views that will show all guests.
For ALL guests, this would be the URL /guests, which should map to GuestsController#index:
# guests_controller.rb
class GuestsController < ApplicationController
...
def index
#guests = Guest.all # or some scope like Guest.active
end
...
end
For guests related to a give user, this would be the URL /users/:id/guests which should map to UsersController#guests:
# users_controller.rb
class UsersController < ApplicationController
before_action :set_user, only: %i[show edit update guests]
...
def guests
#user.guests
...
end
...
private
# this method sets #user for all views defined in the `:only` hash of the `before_action` callback.
def set_user
#user = User.find(params[:id]
end
I would like each guest row to display the user who has entered their information.
Since you have a user_id field on Guest, if you switch to a has_many belongs_to relationship, then you can just call the user:
<tbody>
<% #guests.each do |guest| %>
<tr style="background-color:<%=guest.mood%>">
<td> <%= link_to guest.guest_name, "/guests/#{guest.id}" %></td>
<td><%= guest.experience %></td>
<td><%= guest.room_num %></td>
<td><%= guest.arrival_date %></td>
<td><%= guest.departure_date %></td>
<td ><%= guest.user%></td> <!-- guest.user instead of #user.current_user -->
<td></td>
<td><%= guest.interaction %></td>
</tr>
<% end %>
</tbody>
Extra credit: use Includes to pre-load associations
Also, as a pro tip, calling guest.user could get slow because each guest record needs to make a call to the User table.
Rails offers eager loading for just this situation.
Change #guests = Guest.all to #guest = Guest.includes(:user).all and Rails will handle the rest.
I was building a rails app. I want to all post on a page. I am able to show posts title and body but i am not able users name(to whom post belongs -owner of post).
Post-Model
class Post < ApplicationRecord
belongs_to :user
end
User-Model
class User < ApplicationRecord
has_many :posts
end
Posts-Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
end
Posts-View index.html.erb
<h1> All article </h1>
<% #posts.each do |post| %>
<ul>
<div class="row">
<div class="col-md-offset-3 col-md-5">
<h5><b><%= post.title %></b> by <%= %></h5>
<p><%= post.body %></p>
</div>
</div>
</ul>
<% end %>
Schema looks like
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Display name of user who posted that post
Since you have a relation to the User in the Post as belongs_to :user, So, you can simply use post.user.name to get the name of the user like.
<h1> All article </h1>
<% #posts.each do |post| %>
<ul>
<div class="row">
<div class="col-md-offset-3 col-md-5">
<h5><b><%= post.title %></b> by <%= post.user.name %></h5>
<p><%= post.body %></p>
</div>
</div>
</ul>
<% end %>
bonus
If you want to eager load the user in a single query you might wanna use. Post.includes(:user).all, its better if you always use the user, saves you extra query. Mind that it has some downsides to it as well.
Try post.user to get user object. Then you can print any attribute from the user object.
<p><%= post.user.name %></p>
And use includes method to avoid N + 1 queries problem.
def index
#posts = Post.includes(:user)
end
Reference: https://guides.rubyonrails.org/active_record_querying.html
It's quite easy to access. Try post.user.name and you will get name of associated user.
Read here about all standard methods available for belongs_to association
https://guides.rubyonrails.org/association_basics.html#methods-added-by-belongs-to
I am building a family album application with Ruby on Rails. I have two main tables: "families" and "members." families has_many members.
I when I create a new "member" of a "family," I am attempting to track that new members relationship to the original member. My app allows you to create one original member, then all other members must be created from existing member's show pages. The idea is to not allow any new members to be added to the family without having a relationship established with an existing member. This will avoid floater members who are not related to anyone else.
I have a column called "parent_id." When a new member is created, you can add the current member's id (form is in member show view) as the parent_id for the new member. This links the records so they can be referenced later.
My question is fairly simple. In a member's show view, I want a table to display all members who have a parent_id == the current member's id. Pretty simple, but I can't get it to work. This is what I have now:
<table class="table table-striped">
<tr>
<td>First</td>
<td>Last</td>
<td>Birthplace</td>
</tr>
<% #members.where("parent_id == #member.id").each do |p| %>
<tr>
<td><%= p.first_name %></td>
<td><%= p.last_name %></td>
<td><%= p.birthplace %></td>
</tr>
<% end %>
</table>
This is the error this code throws:
SQLite3::SQLException: near ".": syntax error: SELECT "members".* FROM "members" WHERE "members"."family_id" = ? AND (parent_id == #member.id)
Here is the show action in my members controller:
def show
#family = Family.find(params[:family_id])
#member = Member.find(params[:id])
#new = Member.new
#members = #family.members
end
Here is the schema for my two tables:
create_table "families", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "members", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "birthplace"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "family_id"
t.integer "parent_id"
t.integer "child_id"
t.integer "sibling_id"
end
You're trying to access to an id attribute within the #members object, which probably is an ActiveRecord_Associations_CollectionProxy from the Member model.
If you make an #members.inspect you'll see every element inside of it, as an array value has an id but not the object itself.
You could get each member id attribute if you iterate over every element and then in that way you're able to make the comparison parent_id = member.id, which could be safer if you do 'parent_id = ?', member.id to avoid SQLi.
But I think you could check easier the members on #members just comparing its attributes:
<% #members.each do |member| %>
<% if member.parent_id == member.id %>
<tr>
<td><%= member.first_name %></td>
<td><%= member.last_name %></td>
<td><%= member.birthplace %></td>
</tr>
<% end %>
<% end %>
I have been struggling with this problem for much too long and am hoping that one of you will be able to see what I cannot. It has to be a simple stupid error since I do this everywhere in the application with no errors.
Problem: I have a billing_type (reference) table which contains a number of records. I also have a billing table which contains bills. When I display the billing table in a list, the billing_type is show as:
"#<BillingType:0x00000006f49470>"
which I am assuming is its pointer.
The billing_type model :
class BillingType < ActiveRecord::Base
validates :billing_type, presence: true, uniqueness: true
has_many :billings
accepts_nested_attributes_for :billings
end
The billing model :
class Billing < ActiveRecord::Base
belongs_to :billing_type
belongs_to :horse
validates :billing_type_id, presence: true
validates :horse_id, presence: true
end
The schema:
create_table "billing_types", force: :cascade do |t|
t.string "billing_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "billing_types", ["billing_type"], name: "index_billing_types_on_billing_type", unique: true
create_table "billings", force: :cascade do |t|
t.date "billing_date"
t.integer "billing_type_id"
t.integer "horse_id"
t.string "notes"
t.decimal "cost"
t.date "date_billed"
t.date "date_received"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
The query in the controller (note: I exported the database and put this in the SQL and it returned everything, including the billing_type, o.k.):
def index
#billings = Billing.find_by_sql("SELECT billings.id, billings.billing_date, horses.horse_name, billings.billing_type_id, billing_types.billing_type, billings.notes, billings.cost, billings.date_billed, billings.date_received FROM billings JOIN horses ON billings.horse_id = horses.id JOIN billing_types ON billings.billing_type_id = billing_types.id ORDER BY billings.billing_date, LOWER(horses.horse_name)")
end
The index page:
.
.
.
<div class = "table-container">
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>Billing date</th>
<th>Horse name</th>
<th>Billing type</th>
.
.
.
</tr>
</thead>
<tbody>
<% #billings.each do |billing| %>
<tr>
<% if billing.billing_date %>
<td><%= billing.billing_date.strftime("%d/%m/%Y") %></td>
<% else %>
<td><%= billing.billing_date %></td>
<% end %>
<td><%= billing.horse_name %></td>
<td><%= billing.billing_type %></td>
.
.
.
</tr>
<% end %>
</tbody>
</table>
Thanking you in advance for any help you can give!
That's exactly what I would expect to see
billing.billing_type is the representation of the the whole BillingType class as an object.
If you wanted that then maybe
billing.billing_type.inspect is what you are expecting but I suspect that what you really want is the name in which case you should be looking to display a property of the object not the object itself. i.e.
billing.billing_type.name
I am working on a rails application where one user class named (submitters) are able to login and once they are logged in they create videos. My videos controller is here:
class VideosController < ApplicationController
def index
#videos = Video.find :all
end
def new
#submitter = current_submitter
#video = #submitter.videos.build
end
def create
#submitter = current_submitter
#video = #submitter.videos.build(params[:video])
if #video.save
#video.convert
flash[:notice] = 'Video has been uploaded'
redirect_to :action => 'index'
else
render :action => 'new'
end
end
def show
#video = Video.find(params[:id])
end
def destroy
#video = Video.find(params[:id])
#video.destroy
flash[:notice] = "Successfully deleted the video."
redirect_to root_url
end
def update_date
#video = Video.find(params[:id])
#video.update_attributes(params[:video])
flash[:notice] = "Successfully added a launch date!"
redirect_to #video
end
end
As you can probably see, I am trying to construct the controller so that when a video is created, it is created as belonging to the submitter who upload the video (via the video new view). I am using a auth system with a current_submitter method written in the application controller.
Now it lets me upload a video fine when I am logged in as a submitter. The trouble for me is working out how to display information in my view. If I want to display some columns with information about the video and then others with information about the submitter who uploaded the video, how do I go about doing that from the controller (index action), into the index view. My current view which does not work in below:
<% title "Films Submitted" %>
<table>
<tr>
<th>Title</th>
<th>Film Type</th>
<th>Premiere</th>
<th>Company</th>
<th>Name</th>
</tr>
<% for video in #videos do %>
<tr>
<td><%= link_to video.title, video %></td>
<td><%= video.film_type %></td>
<% if video.premiere == "true" %>
<td>Premiere</td>
<% else %>
<td><%= %></td>
<% end %>
<td><%= video.submitter.company %></td>
<td><%= video.submitter.name %></td>
<td><%= link_to "Delete", video, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br><br>
<%= link_to "Upload a Video", new_video_path %>
Any suggestions or tips from rails developers would be much appreciative... I am new and trying to learn.
Video Model:
class Video < ActiveRecord::Base
belongs_to :submitter
has_attachment :content_type => :video,
:storage => :file_system,
:max_size => 50.megabytes
end
Submitter Model:
class Submitter < ActiveRecord::Base
acts_as_authentic
has_many :videos
end
Schema:
create_table "videos", :force => true do |t|
t.string "title"
t.text "description"
t.string "state"
t.datetime "created_at"
t.datetime "updated_at"
t.string "content_type"
t.integer "size"
t.string "filename"
t.string "film_type"
t.boolean "premiere", :default => false
t.date "preferred_date"
t.text "reason"
t.integer "submitter_id"
t.date "actual_date"
end
create_table "submitters", :force => true do |t|
t.string "name"
t.string "company"
t.string "email"
t.string "username"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "video_id"
end
I think you've probably only setup your associations in one direction. You already have:
class Submitter < ActiveRecord::Base
has_many :videos
end
But you also need to reciprocate that relationship in the video model:
class Video < ActiveRecord::Base
belongs_to :submitter
end
This will create the submitter method in your video model, that references the submitter it belongs to.
UPDATE:
After you updated your question with the model info, I noticed one small thing: You have a video_id column on your submitters table that doesn't need to be there. Only the "belongs_to" side of the association needs a foreign key. But this isn't causing your problem.
I think you might have a video in your database without a submitter! In the index action, since you're not checking for a valid submitter before you start using it, if even a single video record is missing a submitter_id, it'll break the view.