I have a Fantasy Soccer Rails app and i'm trying to add a feature, which is to each user make a draft choice, one user at a time, one after the other; how can i make that?
A user creates a lineItem refering to each player selected to save him to his personal squad.
Here is some of the code that i think is regarding the feature but more is needed please tell me.
Thanks
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one :squad
end
users_controller.rb
class UsersController < ApplicationController
def update
skip_authorization
#user = current_user
#user.update(user_params)
redirect_to profile_path
end
def user_params
params.require(:user).permit(:first_name, :last_name, :mobile, :drft_pass, :drft_shortlist)
end
has_one :squad
end
line_item.rb
class LineItem < ApplicationRecord
belongs_to :player
belongs_to :squad
end
line_items_controller.rb
class LineItemsController < ApplicationController
before_action :set_line_item, only: [:destroy]
before_action :set_player, only: [:create]
def create
##player_id = #player
#chosen_player = #player
##squad_id = #squad
#current_squad = #current_squad
if LineItem.where(squad_id: params[:squad_id]).where(player_id: params[:player_id]).count > 0
message = 'This player is already on your Squad'
elsif
LineItem.where(player_id: params[:player_id]).present?
message = 'This player is already selected by other Coach'
else
#line_item = LineItem.new
#player = Player.find(params[:player_id])
#squad = Squad.find(params[:squad_id])
#line_item.squad = #squad
#line_item.player = #player
#line_item.save
message = 'Success. The player was added to your Squad'
end
redirect_to players_path
flash[:notice] = message
end
def destroy
#line_item.destroy
redirect_to user_squad_path(current_user.id, :squad_id)
end
private
def line_item_params
params.require(:line_item).permit(:player_id, :squad_id)
end
def set_line_item
#line_item = LineItem.find(params[:id])
end
def set_player
#player = Player.find(params[:player_id])
end
end
player.rb
class Player < ApplicationRecord
include PgSearch::Model
pg_search_scope :search_by_full_name, against: [:name, :club, :pos]
end
players_controller.rb
class PlayersController < ApplicationController
#before_action :set_players, only: [:show, :edit, :update, :destroy]
def index
if params[:term]
#players = Player.search_by_full_name(params[:term]).paginate(page: params[:page], per_page: 20)
else
#players = Player.paginate(page: params[:page], per_page: 20)
end
end
def show
#player = Player.find(params[:id])
end
private
def player_params
params.require(:player).permit()
end
def set_player
#player = Player.find(params[:id])
end
end
squad.rb
class Squad < ApplicationRecord
has_one_attached :photo
belongs_to :user
has_many :line_items, dependent: :destroy
has_many :players, through: :line_items
validates :name, presence: true
end
squads_controller.rb
class SquadsController < ApplicationController
before_action :authenticate_user!
#before_action :set_squad
def show
end
def new
#user = User.find(params[:user_id])
#squad = Squad.new
end
def create
#user = User.find(params[:user_id])
#squad = Squad.new(squad_params)
#squad.user = #user
if #squad.save
redirect_to user_squad_path(current_user.id, #squad)
flash[:notice] = 'Success. Your squad was created'
else
render "new"
flash[:notice] = 'Squad not created; please try again'
end
end
def edit
#user = User.find(params[:user_id])
#squad = Squad.find(params[:squad_id])
end
def update
#user = User.find(params[:user_id])
#squad = Squad.find(params[:id])
#squad.update(squad_params)
redirect_to user_squad_path(current_user.id, #squad)
end
def destroy
#squad = Squad.find(params[:squad_id])
#squad.destroy
redirect_to root_path
end
private
def set_squad
#squad = #current_squad
end
def squad_params
params.require(:squad).permit(:name, :sigla, :photo)
end
end
OK, so there's a lot going on here - far too much for us to help with in a single question. Saying "This doesn't work, please fix it" isn't going to get you useful answers. What, exactly, isn't working? How isn't it working? And what exactly do you want help with?
Some easy tips to start you off, though. First, you could make a lot better use of Rails built-in helpers. For example, in your line_items_controller, try something like this:
def create
line_item = LineItem.find_by(player_id: line_item_params[:player_id])
if line_item.present?
message = if line_item.squad_id == current_user.squad_id
'This player is already on your Squad'
else
'This player is already selected by other Coach'
end
else
#line_item = LineItem.new
#line_item.squad = current_user.squad
#line_item.player = Player.find(line_item_params[:player_id])
message = if #line_item.save
'Success. The player was added to your Squad'
else
"Error - couldn't save your selection"
end
end
flash[:notice] = message
redirect_to players_path
end
Notice that using current_user.squad will return the related record, as will squad.line_items return the related records. This means (a) you don't have to write queries and (b) you'll always protect yourself against accidentally allowing a user to access records they shouldn't have access to (like other people's squads).
I've also switched around the logic of your if block to reduce the number of times you query the database. First, find the LineItem containing this player (using find_by rather than where because, presumably, there should be only one such record because a Player should only be in one Squad). If there are any records, check to see whether the squad for that LineItem matches the current_user's squad and adjust the message depending.
Then, assign the Player to the user's Squad if they're available, pretty much as you did before. You're not using strong params, even though you've defined the required method in e.g. the line_items_controller. You'll note that I've inserted this reference into the example above. The big change to this block is that it's best to avoid using the squad parameter because (presumably) each user can only create LineItems for their own squad! So I've swapped this for current_user.squad.
And note that you need to set your flash message before redirecting - that's effectively the end of your method.
That gives you one (!) of your methods cleaned up and hopefully doing what you want. But, overall, as I said, you're probably going to have to tell us exactly what the problem is that you're having.
Related
I am creating a webiste where people can debate with each other. It has 4 main models - post, for_the_motion, against_the_motion, and user( added in the respective order). I ran a migration and made a association between for model and against model.
For each view in "for" model I want to show which user added that particular motion. But I am getting an error
undefined method `image_url' for nil:NilClass
Stuck from long time on this. This is how the models look
user.rb
class User < ApplicationRecord
has_many :posts
has_many :fors
has_many :againsts
class << self
def from_omniauth(auth_hash)
user = find_or_create_by(uid: auth_hash['uid'], provider: auth_hash['provider'])
user.name = auth_hash['info']['name']
user.image_url = auth_hash['info']['image']
user.url = auth_hash['info']['urls'][user.provider.capitalize]
user.save!
user
end
end
end
for.rb
class For < ApplicationRecord
belongs_to :post, optional: true
belongs_to :user,optional: true
end
post.rb
class Post < ApplicationRecord
has_many :fors, dependent: :destroy
has_many :againsts, dependent: :destroy
belongs_to :user, optional: true
end
against.rb
class Against < ApplicationRecord
belongs_to :post, optional: true
belongs_to :user, optional:true
end
CONTROLLERS
posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.all
end
def land
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
redirect_to #post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:title)
end
end
fors_controller.rb
class ForsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#for = #post.fors.create(fors_params)
#for.user = current_user
redirect_to post_path(#post)
end
private
def fors_params
params.require(:for).permit(:content)
end
end
sessions_controller.rb
class SessionsController < ApplicationController
def create
begin
#user = User.from_omniauth(request.env['omniauth.auth'])
session[:user_id] = #user.id
# flash[:success] = "Welcome, #{#user.name}!"
rescue
# flash[:warning] = "There was an error while trying to authenticate you..."
end
redirect_to root_path
def destroy
if current_user
session.delete(:user_id)
# flash[:success] = 'See you!'
end
redirect_to root_path
end
end
end
This is where I am getting the error
<h1><%=#post.title%></h1>
<div class="fort">
<h3>For the motion</h3>
<%#post.fors.each do |f|%>
<p><%=f.content%></p>
<p><%=f.user.image_url%></p>/*This is where errors arise*/
<%end%>
<%= render "fors/form"%>
</div>
<div class="against">
<h3>Against the motion</h3>
<%#post.againsts.each do |f|%>
<p><%=f.content%></p>
<p><%= #post.user.name%></p>
<%end%>
<%= render "againsts/form"%>
</div>
Here is the github link for any other required information
https://github.com/sarfrazbaig/DebatingSociety2
Seems like you missed saving the .user on fors_controller.rb:
class ForsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#for = #post.fors.create(fors_params)
# .create above already will save a new For record in DB
# therefore your #for.user assignation will be only assigned in memory, but not yet in DB
#for.user = current_user
# you'll need to save it again afterwards:
#for.save
redirect_to post_path(#post)
end
# ...
end
Suggestion:
use .new instead of .create to not-yet-save into the DB, and only call save when everything that you need to assign is already assigned.
class ForsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#for = #post.fors.new(fors_params)
#for.user = current_user
#for.save
redirect_to post_path(#post)
end
# ...
end
Take note that you would still encounter that error even if you already updated your code with the above; this is because currently your For records in the DB all are missing the .user value. You'll have to manually assign and save the .user accordingly for each For record, and probably best that you'd write a...
class For < ApplicationRecord
validates :user, presence: true
end
... validation so that this error will be prevented in the future.
One of the #post.fors is lacking a user, which is permitted by the belongs_to :user, optional: true in your For model.
You can restrict your query to showing only fors that have an associated user:
#post.fors.joins(:users) or you can use the safe navigation operator to return nil when attempting to read the image_url for a non-existent user - f.user&.image_url
I have a User model. A user can be an employer or a student. So there is an employer model and a student model. They both belong to user. Only employers can view student profiles. So if there is something wrong with the profile, the employer should be able to report the profile. I was thinking of having a "report" button on the profile which only the employers can see. Then when they click on it, the admin (me) gets an email with the url or the id of the student.
Right now, the student profile url looks like www.mywebsite.com/students/john-big. How can the report button be setup so the whole URL or the user-id (John-big) gets emailed to me.
The mailer is set up already because I set it up in a way where I get an email every time a user signs up. I can use the same logic to email myself, but grabbing the ID or url is the problem. What is the best way to do it?
Userinfo controller (userinfo =student):
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy, :log_impression]
before_action :authenticate_user!
def index
end
def show
end
def new
#userinformation = current_user.build_userinfo
end
def create
#userinformation = current_user.build_userinfo(userinfo_params)
if #userinformation.save
redirect_to userinfo_path(#userinformation)
else
render 'new'
end
end
def edit
end
def update
if #userinformation.update(userinfo_params)
redirect_to userinfo_path(#userinformation)
else
render 'edit'
end
end
def destroy
#userinformation.destroy
redirect_to root_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major)
end
def find_userinfo
#userinformation = Userinfo.friendly.find(params[:id])
end
end
Employer controller:
class EmployersController < ApplicationController
before_action :find_employer, only: [:show, :edit, :update, :destroy]
def index
end
def show
end
def new
#employer = current_user.build_employer
end
def create
#employer = current_user.build_employer(employer_params)
if #employer.save
redirect_to userinfos_path
else
render 'new'
end
end
def edit
end
def update
if #employer.update(employer_params)
redirect_to employer_path(#employer)
else
render 'edit'
end
end
def destroy
#employer.destroy
redirect_to root_path
end
private
def employer_params
params.require(:employer).permit(:paid, :name, :company, :position, :number, :email, :emp_img)
end
def find_employer
#employer = Employer.friendly.find(params[:id])
end
end
User model:
class User < ActiveRecord::Base
has_one :userinfo
has_one :employer
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
acts_as_messageable
after_create :welcome_send
def welcome_send
WelcomeMailer.welcome_send(self).deliver_now
end
end
Please let me know if you guys need more information.
I would use request.url() to get the URL of your view (the student profile url).
Try adding this to you view to get a feeling of it:
<%= debug("request.url: #{request.url()}") if Rails.env.development? %>
I hope this helps.
I have some polymorphic relationships set up and are working well for the primary purpose. That is for a User to be able to Comment on both Articles and Coffeeshops.
However I'm struggling with being able to display the users list of comments on their profile page. In the future I also want the user to be able to 'favourite' and 'want to go to' different coffeeshops which I would also want to show up on their profile page. I'm hoping once I get the logic for display current comments, the rest will be a breeze ;)
So what I have:
Models
class User < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :commentable, polymorphic: true
end
class Coffeeshop < ApplicationRecord
has_many :comments, as: :commentable
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
Comment Controller
class CommentsController < ApplicationController
before_action :load_commentable
before_action :authenticate_user!
before_action :comment_auth, only: [:edit, :update, :destroy]
def index
#comments = #commentable.comments
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(allowed_params)
#comment.user_id=current_user.id if current_user
if #comment.save
redirect_to #commentable, notice: "Comment created."
else
render :new
end
end
def update
#comment = Comment.find(params[:id])
if #comment.update(comment_params)
redirect_to #commentable
else
render 'edit'
end
end
def destroy
#comment = Comment.find(params[:id])
#commentable = #comment.commentable
if #comment.destroy
flash[:success] = "Comment Destroyed!"
redirect_to :back
end
end
private
def allowed_params
params.require(:comment).permit(:name, :body)
end
def load_commentable
resource, id = request.path.split('/')[1,2]
#commentable = resource.singularize.classify.constantize.find(id)
end
def comment_params
params.require(:comment).permit(:body).merge(user_id: current_user.id)
end
Profile Controller
class ProfileController < ApplicationController
before_action :authenticate_user!
def index
end
def show
#user = User.find.current_user(params[:id])
#comments = #commentable.comments
end
In views/profile/show.html.erb. I was trying to do:
<h3>Your Latest Comment</h3>
<%=#comment.user.body%>
But this clearly isn't right as I get Couldn't find User without an ID. From ProfileController#show
update
If I change ProfileController to
before_action :authenticate_user!
def index
#user = User.find.current_user(params[:user_id])
end
def show
#comments = #commentable.comments
end
I get an error for undefined comments.
ok first return this to show moving it to index is not solving a problem the index is not called so write show like this.
def show
#user = current_user #you get instance of a user that is logged in
#comments = #user.comments
end
I do not know if you have user_id in your comment migration but if you do not have you must write
class User < ApplicationRecord
has_many :comments, as: :commentable
end
view
<h3>Your Latest Comment</h3>
<%=#comments.try(&:last).try(&:body)%>
We've attempted a has_many, belongs_to relationship: we've created a class - ArticleCategory - that belongs to Article. Articles have many article_categories. ArticleCategory has one attribute - sport:string
We are unable to call Article.last.article_categories as it returns this error message:
NoMethodError: undefined method
Here is our relevant code:
ArticleCategories Controller
class ArticleCategoriesController < ApplicationController
before_action :set_article_category, only: [:show, :edit, :update, :destroy]
def index
#article_categories = ArticleCategory.all
respond_with(#article_categories)
end
def show
respond_with(#article_category)
end
def new
#article_category = ArticleCategory.new
respond_with(#article_category)
end
def edit
end
def create
#article = Article.find(params[:article_id])
#article_category = #article.article_categories.build(article_category_params)
# #article_category = ArticleCategory.new(article_category_params)
#article_category.save
respond_with(#article_category)
end
def update
#article_category.update(article_category_params)
respond_with(#article_category)
end
def destroy
#article_category.destroy
respond_with(#article_category)
end
private
def set_article_category
#article_category = ArticleCategory.find(params[:id])
end
def article_category_params
params.require(:article_category).permit(:sport)
end
end
ArticleCategories Model
class ArticleCategory < ActiveRecord::Base
belongs_to :article
end
Articles Controller
class ArticlesController < ApplicationController
load_and_authorize_resource
skip_authorize_resource :only => [:index, :show]
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
# authorize! :create, #article
if #article.save
#send email to referral email
all_users = User.all
all_users.each do |user|
ArticleMailer.article_confirmation(user,#article).deliver
end
redirect_to #article
else
render 'new'
end
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all.reverse
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :date, :kredit, article_categories_attributes: [:id, :sport])
end
end
Articles Model
class Article < ActiveRecord::Base
has_many :comments, dependent: :destroy
has_many :article_categories, dependent: :destroy
accepts_nested_attributes_for :article_categories, :allow_destroy => true
validates :title, presence: true,
length: { minimum: 5 }
end
We can't figure out why we can't call this method on Article.last
Since my comment helped, I'll add this as an answer so we can close this off
If you had the console open when you made changes to the model, those changes aren't reflected yet until you either exit and re-enter the console, or type reload!. Either of these will cause the console to reload all of your classes. In short, your classes remain in the state they were in when you first loaded the console, so its something to keep in mind when you're developing and playing around in the console simultaneously.
Regarding your question from the comments:
When we call with an instance of the article - #article.article_categories - we get this <ArticleCategory::ActiveRecord_Associations_CollectionProxy:0x007fc46acc5db8>
That's correct, you get back an association object. Most of the time you don't need to worry about this, as invoking some array methods such as .each and such will give you the concrete objects.
The collection proxy object, however, lets you perform other active record method calls to further filter the article_categories list if you like. You can do stuff like this, for example:
article.last.article_categories.where(sport: "curling")
and the article_categories list will be limited to those that match the .where clause filter. You can verify this in the console or rails log by looking at the generated SQL query.
Hope that helps.
I have 2 controllers User and Rota. I want the user to be able to create their own Rota but only be able to edit, show and destroy their own. I need to be able to code so that my rotum object belongs to the user object.
ROTA CONTROLLER:
class RotaController < ApplicationController
respond_to :html, :xml, :json
before_action :set_rotum, only: [:show, :edit, :update, :destroy]
def edit
#rotum = #user.rota.find params[:id]
end
def index
#rota = Rotum.all
respond_with(#rota)
end
def show
respond_with(#rotum)
end
def new
#rotum = Rotum.new
respond_with(#rotum)
end
def edit
end
def create
#rotum = Rotum.new(rotum_params)
#rotum.save
respond_with(#rotum)
end
def update
#rotum.update(rotum_params)
respond_with(#rotum)
end
def destroy
#rotum.destroy
respond_with(#rotum)
end
private
def set_rotum
#rotum = current_user.rotums.find(params[:id])
if #rotum.nil?
render :html => "Not authorized", :status => 401
end
end
def rotum_params
params.require(:rotum).permit(:name, :email, :mobile, :category)
end
end
USER CONTROLLER
class UsersController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
def index
#users = User.all
authorize User
end
def show
#user = User.find(params[:id])
authorize #user
end
def update
#user = User.find(params[:id])
authorize #user
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
user = User.find(params[:id])
authorize user
user.destroy
redirect_to users_path, :notice => "User deleted."
end
def edit
#rotum = #user.rota.find params[:id]
end
private
def secure_params
params.require(:user).permit(:role)
end
end
So far my rota allows anyone to create, show, edit and destroy the rota on the rotas page. I only want the user to be able to edit only THEIR OWN rota that they created. For that I have been told tell the rota object to belong to the user object. How can I do this in my controllers or models.
USER MODEL
class User < ActiveRecord::Base
has_many :rota, dependent: :destroy
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
ROTUM MODEL
class Rotum < ActiveRecord::Base
belongs_to :user
end
I get the error:
NoMethodError at /rota/15
undefined method `rotums' for #
You are not showing your models, so I'm assuming that you have a has_many relationship
class User < ActiveRecord::Base
has_many :rota, dependent: :destroy
end
class Rotum < ActiveRecord::Base
belongs_to :user
end
then in your controller you can use the following:
class UsersController < ApplicationController
....
def edit
#rotum = #user.rota.find params[:id]
end
Note that this will raise an ActiveRecord::RecordNotFound exception if a user is triying to edit a rotum that does not belong to him.
You can avoid that problem with the following:
class UsersController < ApplicationController
....
def edit
#rotum = #user.rota.find_by id: params[:id] # returns nil in case the record does not exist or does not belong to #user
redirect_to "somewhere", alert: 'You cannot edit this element' if #rotum.blank?
end