While registration, I want to check whether the given email by a new user already exists or not in my controller.
class LoginsController < ApplicationController
skip_before_action :verify_authenticity_token
def index
#subscriber = Subscriber.new()
end
def sign_up
subscriberNew = Subscriber.new
subscriberNew.name = params[:name]
subscriberNew.cus_user_name = params[:user_name]
subscriberNew.cus_password = params[:password]
subscriberNew.cus_email = params[:email]
subscriberNew.mobile_no = params[:phone]
#if Email exists sends and error message
#...................
#if email does not exist, save the response to database
result = subscriberNew.save
respond_to do |format|
msg = {:status => "ok", :message => "Success!"}
format.json {render :json => msg}
end
end
end
How can I do this?
There are multiple ways to validate unique records, one of the better approaches may be altering your database to set a unique index for the email:
add_index :users, :username, unique: true (in your migration)
The DB index approach is better in long terms performance (see this for example)
You can also validate it in your controller before_action:
before_action :validate_email, only: [:sign_up]
...
private
def validate_email
# Or whatever way of sending a message you prefer
flash[:notice] = "A user with this email already exists"
redirect_to root_path if User.where(email: params[:email]).exists?
end
I'd recommend further reading about Active Record validations in the Rails Guides.
Add a validation for the email with uniqueness: true https://guides.rubyonrails.org/active_record_validations.html#uniqueness
You can do something like:
class Subscriber < ApplicationRecord
validates :email, uniqueness: true
end
and on the action:
subscriberNew.valid?
if subscriberNew.errors[:email].present?
#show_error
else
#success
end
I'd really recommend you to read about rails naming conventions, validations using activerecord and also conventions when creating a form (with form_for helper) and Strong Parameters https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters.
Related
I would like to check if a record exist before_save, what's the best way tod do that ?
def create
#step = Step.new(step_params)
#course = Course.find(step_params[:course_id])
redirect_to course_path(#course) and return if step_already_present?(#step)
if #step.save
redirect_to course_path(#course.id)
else
render :new
end
end
The method to check :
def step_already_present?(step)
Step.where(poi_start_id: step.poi_start_id, course_id: step.course_id).first.present?
end
You can use the uniqueness validation on the Model
If you need to check the two columns together you can use the scope option like this:
class Step < ActiveRecord::Base
validates :poi_start_id, uniqueness: { scope: :course_id }
end
This is my code:
class ApplicationsController < ApplicationController
def new
#application = Application.new
end
def create
#application = Application.new(application_params)
#layout = Layout.find_or_create_by(application_id: #application.id)
if #application.save
redirect_to #application
else
render 'new'
end
end
layout belongs_to :application
When I check the Layouts table it is empty. Can you help me, please?
Your model contains the following validations:
validates :adv_path, presence: true
validates :start_time, presence: true
validates :end_time, presence: true
Therefore you are not able to create a Layout without this values. You must do something like this (with useful values):
Layout.find_or_create_by(id: #application.id) do |layout|
layout.adv_path = 'A useful default'
layout.start_time = 1.second.ago
layout.end_time = 100.year.from_now
end
Or rethink the need for the validators.
In your layout creation line, #application doesn't have an id yet. Resultantly, you pass 'nil' to the #layout's application_id which makes its validation fail. (You mentioned the layout's application presence validation in a comment).
So create the layout after #application is saved and you should be good to go.
if #application.save
#layout = Layout.create(application_id: #application.id)
When you use new method like
#application = Application.new(application_params)
it does not persist that record to db, other words it does not have id. you should use method create instead
#application = Application.create(application_params)
then #application will be persisted to db, and when you say find_or_create_by it will find with id, and not search for id nil
def create
#application = Application.new(application_params)
#layout = Layout.find_or_create_by(application_id: #application.id) # <== that line
if ...
....
end
end
That line is rather misleading. #application.id is nil. So the first time, you'll create a layout record with a application_id as nil. The next time, it'll find the record with application_id: nil and use that. So it'll just create one single record and forever use it.
If you are creating a layout every time you create an application, consider doing it this way:
def create
#application = Application.new(application_params)
if #application.save
#layout = #application.layouts.create( ... ) # assuming application has_many :layouts
redirect_to #application
else
render 'new'
end
end
I am attempting to locate a parent object in a nested controller, so that I can associate the descendant resource with the parent like so:
# teams_controller.rb <snippet only>
def index
#university = Univeresity.find(params[:university_id])
#teams = #university.teams
end
When I call find(params[:university_id]) per the snippet above & in line 6 of teams_controller.rb, I receive ActiveRecord::RecordNotFound - Couldn't find University without an ID.
I'm not only interested in fixing this issue, but would also enjoy a better understanding of finding objects without having to enter a University.find(1) value, since I grant Admin the privilege of adding universities.
The Rails Guides say the following about the two kinds of parameters in a website:
3 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of
parameters possible in a web application. The first are parameters
that are sent as part of the URL, called query string parameters. The
query string is everything after “?” in the URL. The second type of
parameter is usually referred to as POST data. This information
usually comes from an HTML form which has been filled in by the user.
It’s called POST data because it can only be sent as part of an HTTP
POST request. Rails does not make any distinction between query string
parameters and POST parameters, and both are available in the params
hash in your controller:
It continues a little further down, explaining that the params hash is an instance of HashWithIndifferentAccess, which allows usage of both symbols and strings interchangeably for the keys.
From what I read above, my understanding is that Rails recognizes both parameters (URL & POST) and stores them in the same hash (params).
Can I pass the params hash into a find method in any controller action, or just the create/update actions? I'd also be interested in finding a readable/viewable resource to understand the update_attributes method thats called in a controller's 'update' action.
Please overlook the commented out code, as I am actively searching for answers as well.
Thanks in advance.
Here are the associated files and server log.
Webrick
teams_controller.rb
class TeamsController < ApplicationController
# before_filter :get_university
# before_filter :get_team
def index
#university = University.find(params[:univeristy_id])
#teams = #university.teams
end
def new
#university = University.find(params[:university_id])
#team = #university.teams.build
end
def create
#university = University.find(params[:university_id])
#team = #university.teams.build(params[:team])
if #team.save
redirect_to [#university, #team], success: 'Team created!'
else
render :new, error: 'There was an error processing your team'
end
end
def show
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def edit
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def update
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
if #team.update_attributes(params[:team])
redirect_to([#university, #team], success: 'Team successfully updated')
else
render(:edit, error: 'There was an error updating your team')
end
end
def destroy
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
#team.destroy
redirect_to university_teams_path(#university)
end
private
def get_university
#university = University.find(params[:university_id]) # can't find object without id
end
def get_team
#team = #university.teams.find(params[:id])
end
end
team.rb
class Team < ActiveRecord::Base
attr_accessible :name, :sport_type, :university_id
has_many :home_events, foreign_key: :home_team_id, class_name: 'Event'
has_many :away_events, foreign_key: :away_team_id, class_name: 'Event'
has_many :medias, as: :mediable
belongs_to :university
validates_presence_of :name, :sport_type
# scope :by_university, ->(university_id) { where(team_id: team_id).order(name: name) }
# scope :find_team, -> { Team.find_by id: id }
# scope :by_sport_type, ->(sport_type) { Team.where(sport_type: sport_type) }
# scope :with_university, joins: :teams
# def self.by_university(university_id)
# University.where(id: 1)
# University.joins(:teams).where(teams: { name: name })
# end
def self.by_university
University.where(university_id: university_id).first
end
def self.university_join
University.joins(:teams)
end
def self.by_sport_type(sport_type)
where(sport_type: sport_type)
end
def self.baseball
by_sport_type('Baseball/Softball')
end
end
university.rb
class University < ActiveRecord::Base
attr_accessible :address, :city, :name, :state, :url, :zip
has_many :teams, dependent: :destroy
validates :zip, presence: true, format: { with: /\A\d{5}(-\d+)?\z/ },
length: { minimum: 5 }
validates_presence_of :name, :address, :city, :state, :url
scope :universities, -> { University.order(name: 'ASC') }
# scope :by_teams, ->(university_id) { Team.find_by_university_id(university_id) }
# scope :team_by_university, ->(team_id) { where(team_id: team_id).order(name: name)}
def sport_type
team.sport_type
end
end
views/teams/index.html.erb
Placed in gists for formatting reasons
rake routes output: (in a public gist)
enter link description here
rails console
You're not going to want to have both:
resources :universities #lose this one
resources :universities do
resources :teams
end
As for params... you have to give a param. So, when you go to http://localhost:3000/teams there are no params, by default. If you go to http://localhost:3000/teams/3 then params[:id] = 3 and this will pull up your third team.
Keep in mind the nomenclature of an index. The index action of Teams, is going to list all of the teams. All of them. There is no one University there, so what are you actually trying to find? If anything, you'd have, for your University controller:
def show
#university = University.find(params[:id])
#teams = #university.teams
end
so, the address bar will be showing http://localhost:3000/universities/23, right? params[:id] = 23, then you can find the teams associated with that university.
I want to integrate Paypal within the Devise user registration process. What I want is to have a standard rails form based on the devise resource, that also has custom fields belonging to the user's model.
When a user fills in those fields and clicks on signup, it will be redirected to Paypal, when he clears from paypal and returns to our site then the user data must be created.
For the scenario where the user fill's out the paypal form but doesn't come back to our site, we have to keep record of user before redirecting to Paypal.
For this we can create a flag in user model and use Paypal IPN and when the user transaction notified, set that flag.
But in the case when the user is redirected to Paypal but doesn't complete the transaction, if the user returns to registration and signup again, our model should not throw error saying that the email entered already exists in the table.
How can we handle all these scenarios, is there any gem or plugin available to work with?
Here i am posting the detail code for performing the whole process.
registration_controller.rb
module Auth
class RegistrationController < Devise::RegistrationsController
include Auth::RegistrationHelper
def create
#user = User.new params[:user]
if #user.valid?
redirect_to get_subscribe_url(#user, request)
else
super
end
end
end
end
registration_helper.rb
module Auth::RegistrationHelper
def get_subscribe_url(user, request)
url = Rails.env == "production" ? "https://www.paypal.com/cgi-bin/webscr/?" : "https://www.sandbox.paypal.com/cgi-bin/webscr/?"
url + {
:ip => request.remote_ip,
:cmd => '_s-xclick',
:hosted_button_id => (Rails.env == "production" ? "ID_FOR_BUTTON" : "ID_FOR_BUTTON"),
:return_url => root_url,
:cancel_return_url => root_url,
:notify_url => payment_notifications_create_url,
:allow_note => true,
:custom => Base64.encode64("#{user.email}|#{user.organization_type_id}|#{user.password}")
}.to_query
end
end
payment_notification_controller.rb
class PaymentNotificationsController < ApplicationController
protect_from_forgery :except => [:create]
layout "single_column", :only => :show
def create
#notification = PaymentNotification.new
#notification.transaction_id = params[:ipn_track_id]
#notification.params = params
#notification.status = "paid"
#custom = Base64.decode64(params[:custom])
#custom = #custom.split("|")
#user = User.new
#user.email = #custom[0]
#user.organization_type_id = #custom[1].to_i
#user.password = #custom[2]
if #user.valid?
#user.save
#notification.user = #user
#notification.save
#user.send_confirmation_instructions
end
render :nothing => true
end
def show
end
end
I have three fields in one form and two fields in another (same as the earlier form, but just missing one field). I want to validate only two fields in the smaller form, but the issue is that it is validating all the three.
I have written the following logic:
**
class User < ActiveRecord::Base
validate :validate_form #for form with 2 fields
private
def validate_form
if :classify_create
self.errors.add(:weight, "need weight") if weight.blank?
self.errors.add(:height, "need height") if height.blank?
end
end
# Validations of attributes (for form with three fields)
validates :weight, :presence => true
validates :height, :presence => true
validates :gender, :presence => true
end
**
and this is my controller action: basically I have written two separate creates:
**# for form with two fields
def classify
#user = User.new
#title = "Classify"
end
def classify_create
#user = User.where("weight = ? and height = ?", params[:weight] ,params[:height])
end
# for form with three fields
def new
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
#title = "Train"
render 'new'
end
end**
When I submit to the two field form, it gives me an error for gender too and redirects to the page with form having three fields. How should I go about it?
Any help will be appreciated.
Regards,
Arun
First, I would not use classify as a method name. You may conflict with a core inflector provided by ActiveSupport. Call it classification or something.
Second, your validation is running on if #user.save in the create method.
In classify_create you use User.where(...) which is a finder method. You're pulling a matching record and setting it to #user. This does not run validation, yet you receive validation errors. You are posting to create, not classify_create. Bad routes will cause this.
Let's address conditional validation first. In your User model, create a variable to act as a bypass switch for your gender validation. Then tell your validation to check if this bypass switch is false before running:
User < ActiveRecord::Base
attr_accessor :skip_gender # defaults to nil (false)
# ...
validates :gender, :presence => true, :if => :validate_gender? # validate if...
# ...
private
def validate_gender?
!self.skip_gender # true = run validation, false = skip validation
end
# ...
end
Next, clean up your controller. Write two create methods, one setting the switch, one not. (This isn't DRY):
def new_classification
# for form with two fields
#user = User.new
#title = "Classify"
end
def new
# for form with three fields
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => 'new' # render three-field form
end
end
def create_classification
#user = User.where(:weight => params[:weight], :height => params[:height])
# ... do something with #user ...
#user.skip_gender = true # tell #user to skip gender validation
if #user.save
redirect_to #user
else
render :action => 'new_classification' # render two-field form
end
end
Next, adjust config/routes.rb to specify routes to your custom methods.
resources :users do
member do
get 'new_classification', :to => 'users#new_classification', \
:as => :new_classification_for
post 'create_classification', :to => 'users#create_classification', \
:as => :create_classification_for
end
end
Now change your two-field form view. Specify where your form is submitted to.
<%= form_for #user, :url => create_classification_for_user_url(#user) do |f| %>
That should get you by with what you have...
Your problem is two-fold:
You're trying to use one controller for two distinct actions.
The Rails validation model is somewhat limited and inflexible, there should be separate validation passes for controller methods and models.
The easy solution is to kludge around the limitations with a separate controller:
def create_genderless
# Force the issue to an explicit "unknown" state so that
# "unknown" and "missing" can be differentiated.
params[:user][:gender] = 'U'
# And then punt to the existing `create` method.
create
end
Then a bit more validation in your model for good measure
class User < ActiveRecord::Base
validates :gender, :inclusion => { :in => %w[M F U] }
#...
end
Then update your forms to use UserController#create or UserController#create_genderless as appropriate.