In my app i have two types of users: students and companies.
During the registration process, i manage with the wicked gem, the user is asked to fill in data depending on the accounttype (student or company) he chose.
For example, a student have to fill in his university, a company have to fill in the amount of employers.
When the registration process is finished, everything is fine, but somehow i cant access the data of the user which chose accounttype 2 - company.
The data is stored in two seperated associated models:
For students: accountinfo (ye i know its against convention and should have the name account_info)
For companies: accountinfos_company (ye i know, weird pluralization .. but it does the trick and i am not confused by it)
Everything works fine for the students. I am able to fill in data and the data is properly saved within a nested form. Same goes for the companies but unfortunately i am not able to output the data of accountinfos_company in my app. What i can do is for example:
#user.accountinfo.description
For student-accounts. But if I try:
#user.accountinfos_company.description
The output fails with an error
undefined method `accountinfos_company' for #<User::ActiveRecord_Relation:0x007fc1851e6e20>
SOMEHOW I am able to output the data of the particular user via console like: User.last.accountinfos_company.description. I copied every step I made for the first associated model, the data gets properly saved but I am not able to access it inside the app.
I thought it might be something missing, some definition or sth like this but as far as i can see, everything is fine.
users_controller.rb (User association stuff gets created in another controller)
class UsersController < ApplicationController
before_action :authenticate_user!
def index
redirect_to root_path
end
def create
#user = User.create( user_params )
end
def show
#user = User.find(params[:id])
if #user.not_complete?
redirect_to user_steps_path
else
render 'show'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
# might be the right way?
#user.update(user_params)
if #user.update(user_params)
redirect_to User.find(params[:id]), notice: 'Profil bearbeitet.'
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :accounttype,
:accountinfo, :accountinfos_company, :profile_image, :active, accountinfo_attributes:[:id], accountinfos_company_attributes:[:id])
end
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :welcome, :info
before_action :authenticate_user!
def show
#user = current_user
render_wizard
end
def create
#user = current_user
if #user.accounttype == 1
#accountinfo = #user.accountinfo.create(user_params)
elsif #user.accounttype == 2
#accountinfos_company = #user.accountinfos_company.create(user_params)
end
end
def update
#user = current_user
#user.update(user_params)
render_wizard #user
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :accountinfo, :accountinfos_company,:profile_image, :active, accountinfo_attributes:[:id, :city, :competence, :description, :university], accountinfos_company_attributes:[:id, :city, :company_type, :description, :company_name, :employer_amount])
end
private
def redirect_to_finish_wizard_path
redirect_to root_path, notice: "Danke für deine Zeit!"
end
end
user.rb
class User < ApplicationRecord
has_one :accountinfo
has_one :accountinfos_company
has_many :offer_posts
has_many :search_posts
accepts_nested_attributes_for :accountinfo, update_only: true
accepts_nested_attributes_for :accountinfos_company, update_only: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :first_name, :last_name, :email, :accounttype
end
accountinfo.rb and accountinfos_company.rb
belongs_to :user
I dont know if i have something missed but yeah.
If you could help me i would be really glad.
Related
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.
First this is all of my code
#models/user.rb
class User < ApplicationRecord
has_many :trips
has_many :homes, through: :trips
has_secure_password
accepts_nested_attributes_for :trips
accepts_nested_attributes_for :homes
validates :name, presence: true
validates :email, presence: true
validates :email, uniqueness: true
validates :password, presence: true
validates :password, confirmation: { case_sensitive: true }
end
#home.rb
class Home < ApplicationRecord
has_many :trips
has_many :users, through: :trips
validates :address, presence: true
end
class HomesController < ApplicationController
def show
#home = Home.find(params[:id])
end
def new
if params[:user_id]
#user = User.find_by(id: params[:user_id])
#home = #user.homes.build
end
end
def create
#user = User.find_by(id: params[:user_id])
binding.pry
#home = Home.new
end
private
def home_params
params.require(:home).permit(:address, :user_id)
end
end
I am trying to do something like this so that the home created is associated with the user that is creating it.
def create
#user = User.find_by(id: params[:user_id])
#home = Home.new(home_params)
if #home.save
#user.homes << #home
else
render :new
end
end
The problem is that the :user_id is not being passed into the params. So the #user comes out as nil. I can't find the reason why. Does this example make sense? Am I trying to set the associations correctly? Help or any insight would really be appreciated. Thanks in advance.
The way you would typically create resources as the current user is with an authentication such as Devise - not by nesting the resource. Instead you get the current user in the controller through the authentication system and build the resource off it:
resources :homes
class HomesController < ApplicationController
...
# GET /homes/new
def new
#home = current_user.homes.new
end
# POST /homes
def create
#home = current_user.homes.new(home_parameters)
if #home.save
redirect_to #home
else
render :new
end
end
...
end
This sets the user_id on the model (the Trip join model in this case) from the session or something like an access token when dealing with API's.
The reason you don't want to nest the resource when you're creating them as a specific user is that its trivial to pass another users id to create resources as another user. A session cookie is encrypted and thus much harder to tamper with and the same goes for authentication tokens.
by using if params[:user_id] and User.find_by(id: params[:user_id]) you are really just giving yourself potential nil errors and shooting yourself in the foot. If an action requires a user to be logged use a before_action callback to ensure they are authenticated and raise an error and bail (redirect the user to the sign in). Thats how authentication gems like Devise, Knock and Sorcery handle it.
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.
One thing that confuses me the most is when doing validation in one model with two controllers. I have a login system which register and logs users in. There both use the same model but both does not use the same amount of HTML widgets. One controller contains password, retype password, user name, first & second name and so on. The second controller uses only the user name and password fields. How would you do validation in the same model for this situation?
Thank you
here is the controller that register new users:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to '/cool'
else
#user = Newuser.new
#user.valid?
#user.errors.messages
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
second controller:
class LoginsController < ApplicationController
before_filter :authorize
def index
#rentals = Rental.where(user_id: current_user.id).limit(5)
#buys = Buy.where(user_id: current_user.id).limit(5)
#users = User.where(id: current_user.id)
#buyGames = BuyGame.where(user_id: current_user.id).limit(5)
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to '/logout'
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(account_params)
redirect_to '/cool'
else
render 'edit'
end
end
private
def account_params
params.require(:user).permit(:name, :email, :password, :salt, :firstname, :secondname, :address, :postcode)
end
end
Here is my model:
class User < ApplicationRecord
has_secure_password
end
One way to go is to remove validations from the model and put them in form objects. For this case, you'll have two form objects, each with its own set of validations. And you use the appropriate one in respective controllers. Something along these lines:
# logins_controller
def update
login_form = FormObjects::LoginForm.new(login_params)
if login_form.valid?
redirect_to '/cool'
else
render 'edit'
end
end
# users controller
def create
signup_form = FormObjects::SignupForm.new(user_params)
if signup_form.save
redirect_to '/cool'
else
render 'new'
end
end
# signup_form
module FormObjects
class SignupForm
include ::ActiveMode::Model
validate_presense_of :email, :password, :password_confirmation, :address, :whatever_else
def save
# create user here
end
end
end
# login_form
module FormObjects
class LoginForm
include ::ActiveMode::Model
validate_presense_of :email, :password
end
end
You can simply specify validations on actions, that is:
validates :first_name, presence: true, on: :create # which won't validate presence of first name on update or any other action
I believe the trick you are looking for is to define validation actions on create/update of the model. Something roughly along these lines:
class User < ActiveRecord::Base
# These are example validations only; replace with your actual rules.
validates :password, confirmation: true
validates_presence_of :username
validates :first_name, presence: true, format: {with: /.../}, on: create
validates :last_name, presence: true, format: {with: /.../}, on: create
end
...However, I am unclear why you would want to do this in your specific example. It would be advisable to always run all validation checks on fields like first_name, to help maintain data integrity.
I'm trying to create a Profile model upon my Users registering for the site. As I have it now the Profile model is being created upon registration with the correct foreign key. My problem lies with trying to update the Profile model after the user has gone through the Devise confirmation steps.
My users are called "artists".
### /artists/registrations_controller.rb ###
class Artists::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
super
#profile = #artist.build_artist_profile
end
# POST /resource
def create
super
#profile = #artist.create_artist_profile(profile_params)
end
private
def profile_params
params.permit(:biography, :location, :genre, :members, :facebook_url, :twitter_url, :youtube_url, :itunes_url, :amazon_url)
end
end
### /artists/profiles_controller ###
class Artists::ProfilesController < ApplicationController
before_action :authenticate_artist!
before_action :correct_artist
before_action :set_artist
def edit
#profile = ArtistProfile.find_by(params[:artist_id])
end
def update
#profile = ArtistProfile.find_by(params[:artist_id])
if #profile.update_attributes(profile_params)
redirect_to current_artist
else
redirect_to root_url
end
end
private
def set_artist
#artist = current_artist
end
def correct_artist
#artist = current_artist
if #artist != Artist.find(params[:id])
redirect_to artist_path
end
end
def profile_params
params.require(:artist_profile).permit(:biography, :location, :genre, :members, :facebook_url, :twitter_url, :youtube_url, :itunes_url, :amazon_url)
end
end
### /artist.rb ###
class Artist < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable, :timeoutable
has_one :artist_profile, dependent: :destroy
### /artist_profile.rb ###
class ArtistProfile < ActiveRecord::Base
belongs_to :artist
validates :artist_id, presence: true
end
I put my own code into the Devise registration controller in the create method. Upon registration the ArtistProfile model is created and populated with blank strings, which is prefect. However, if I try to edit/update the individual artist's profile only the very first artist's profile gets updated.
ie. Artist 1 signs up and profile 2 is created. Artist 1 updates Profiles 1's location to Buffalo via the edit page. Artist 2 signs up and Profile 2 is created. Artist 2 updates Profile 2's location to New York, but Profile 1's location is updated, not Profile 2's.
Is this the way to create a model upon registration, and if so, how do I fix the edit/update methods?
Or is there a better way altogether?
This line of your code is incorrect:
#profile = ArtistProfile.find_by(params[:artist_id])
A fix is to find the artist, then get the profile:
#profile = Artist.find(params[:artist_id]).artist_profile
An optimization:
#artist = Artist.find(params[:artist_id]).includes(:artist_profile)
#profile = #artist.artist_profile
Or, if your controller is receiving the artist profile id, then you can do this fix:
#profile = ArtistProfile.find(params[:artist_profile_id])