I have a one to one relationship with a simple users model and a profiles model:
models/user
class User < ActiveRecord::Base
authenticates_with_sorcery!
attr_accessible :email, :password, :password_confirmation
has_one :profile, :dependent => :destroy
validates_presence_of :password, :on => :create
validates :password, :confirmation => true,
:length => { :within => 6..100 }
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, :presence => true,
:format => { :with => email_regex },
:uniqueness => {:case_sensitive => false},
:length => { :within => 3..50 }
end
models/profile
# == Schema Information
#
# Table name: profiles
#
# id :integer not null, primary key
# weight :decimal(, )
# created_at :datetime
# updated_at :datetime
#
class Profile < ActiveRecord::Base
attr_accessible :weight
belongs_to :user
end
I am doing it this way because I would like users to be able to track weight over time as well as store other more static data like height in profiles.
However, my new and create methods don't seem to be working correctly. I on submit of the new action I get this error:
undefined method `build' for nil:NilClass
profile_controller
class ProfilesController < ApplicationController
def new
#profile = Profile.new if current_user
end
def create
#profile = current_user.profile.build(params[:profile])
if #profile.save
flash[:success] = "Profile Saved"
redirect_to root_path
else
render 'pages/home'
end
end
def destory
end
end
and profile view for new
<%= form_for #profile do |f| %>
<div class="field">
<%= f.text_field :weight %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
Thanks in advance for any help you might be able to give. Noob here!
The build syntax for has_one association is different from has_many association.
Change your code as follows:
#profile = current_user.build_profile(params[:profile])
Reference: SO Answer
Related
Hi this question is basically the same as this one, which had no responses. I'm trying to combine the Devise registration form to include fields that produce not only a "user", but a "customer" object, an "account" object for that customer, and an "address" for that customer.
When visitor clicks "Sign Up", the registration form should include the standard Devise stuff, but also the fields for the creation of the other objects. Instead, I get this error:
NoMethodError in Registrations#new
undefined method `build_address' for #
Extracted source (around line #6):
<div class="panel panel-default" style="width: 14em;">
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
<%= form_for(resource, as: resource_name, url: >registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<h3>User Info</h3>
Rather than explaining all the relationships, here are the models:
user.rb
class User < ActiveRecord::Base
before_create :generate_id
# Virtual attribute for authenticating by either username or email
# This is in addition to a real persisted field like 'username'
attr_accessor :login
has_one :administrator
has_one :customer
has_many :accounts, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :accounts, :allow_destroy => true
has_one :address, through: :customer
accepts_nested_attributes_for :customer, :allow_destroy => true
accepts_nested_attributes_for :address, :allow_destroy => true
validates_uniqueness_of :email, :case_sensitive => false
validates_uniqueness_of :id
validates :username,
:presence => true,
:uniqueness=> {
:case_sensitive => false
}
# User ID is a generated uuid
include ActiveUUID::UUID
natural_key :user_id, :remember_created_at
belongs_to :user
# specify custom UUID namespace for the natural key
uuid_namespace "1dd74dd0-d116-11e0-99c7-5ac5d975667e"
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :timeoutable, :recoverable, :trackable, :validatable
# Generate a random uuid for new user id creation
def generate_id
self.id = SecureRandom.uuid
end
# Allow signin by either email or username ("lower" function might have to be removed?)
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions.to_h).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
else
where(conditions.to_h).first
end
end
end
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
has_one :address
has_many :accounts
validates :phone1, :firstname, :lastname, presence: true
end
account.rb
class Account < ActiveRecord::Base
belongs_to :customer
belongs_to :user
has_one :acct_type
has_many :acct_transactions
validates :acct_type, presence: true
end
address.rb
class Address < ActiveRecord::Base
belongs_to :customer
belongs_to :user
validates :zip_code, presence: true
validates :address1, presence: true
has_one :zip_code
has_one :state, through: :zip_code
end
The two controllers in question:
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
# GET /users/sign_up
def new
#user = current_user
#customer = nil ##user.customer
#account = nil ##customer.account
#address = nil ##customer.address
# Override Devise default behavior and create a customer, account, and address as well
build_resource({})
resource.build_customer
respond_with self.resource
build_resource({})
resource.build_account
respond_with self.resource
build_resource({})
resource.build_address
respond_with self.resource
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u|
.permit(:username, :email, :password, :password_confirmation,
:customer_attributes => [:phone1, :phone2, :title, :firstname, :lastname],
:account_attributes => :acct_type,
:address_attributes => [:address1, :address2, :zip_code])
}
end
end
addresses_controller.rb (The important parts)
def new
#customer = current_user.customer
#address = #customer.address.build(:customer_id => #customer.id,
:address1 => nil,
:address2 => nil,
:zip_code => nil)
end
def create
#customer = current_user.customer
#address = #customer.address.build(:customer_id => #customer.id,
:address1 => nil,
:address2 => nil,
:zip_code => nil)
respond_to do |format|
if #address.save
format.html { redirect_to #address, notice: 'Address was successfully created.' }
format.json { render :show, status: :created, location: #address }
else
format.html { render :new }
format.json { render json: #address.errors, status: :unprocessable_entity }
end
end
end
And here is the view where the exception is raised (It's really long so actually the important parts):
<h1>Create an account</h1>
<div class="form-group">
<div class="panel panel-default" style="width: 14em;">
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<h3>User Info</h3>
<div class="form-group">
<!-- fields for User object -->
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username, autofocus: true %>
</div>
...
<!-- fields for Customer object -->
<%= f.fields_for :customer do |customer_fields| %>
<div class="field">
<%= customer_fields.label :firstname %>
<%= customer_fields.text_field :firstname %>
</div>
...
<!-- fields for Account object -->
<%= f.fields_for :account do |account_fields| %>
<div class="field">
<%= account_fields.label :acct_type %>
<%= account_fields.text_field :acct_type %>
</div>
<% end %>
<!-- fields for Address object -->
<%= f.fields_for :address do |address_fields| %>
<div class="field">
<%= address_fields.label :address1 %>
<%= address_fields.text_field :address1 %>
</div>
...
The exception is pointing to the block of statements at the top...
<% resource.build_customer if resource.customer.nil? %>
<% resource.build_account if resource.accounts.nil? %>
<% resource.build_address if resource.address.nil? %>
... which has given me trouble before. Before the current error I was getting a similar error from the second line ("build_account"). But that turned out to be a pluralization issue, which I believe I've fixed. Since the HTML is read sequentially, it would seem that there is no problem with the first two build_ methods. Why is there then a problem with the build_address method?
I need to fix this error before I can know if the whole thing will actually work or not. Any ideas?
Thanks
It's Rails 4.1.8 / Devise 3.4.1
The trouble turned out to be the syntax I was using create multiple resource objects. It would pass one, but ignored the rest. What I ended up doing to make it work (or at least make the error go away) was to override the build_resource method to accept an array of parameters for each object to be instantiated:
def new
#user = current_user
build_resource({})
self.resource[:customer => Customer.new, :account => Account.new, :address => Address.new]
respond_with self.resource
end
def build_resource(hash=nil)
hash ||= params[resource_name] || {}
self.resource = resource_class.new(hash)
end
def create
# Override Devise default behavior and create a customer, account, and address as well
resource = build_resource(params[:user])
if(resource.save)
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
render :action => "new"
end
end
Also, I removed the three lines at the top of the form view as they attempted to do some sort of pre-validation in the view and just caused problems. Plenty of validation will happen when the form is submitted. This seems to be doing something good. Now I'm working with the form view and having trouble getting each part to render. Fields_for is rendering fields for User and Account models, but not Customer or Address.
Hi I'm currently working on my first rails project, a site for users to make albums and upload pics. I have the registration, logging in, and friending installed into my app. I'm trying to make it so that in the album creation form, you can see a list of your friends and select who you want to share access to the album with (meaning whoever you select would also be part of #album.users. I'm planning on using a checkbox (I can't think of any better way) to make this selection. However, I am not sure how to link the friendship model with the album/new form. This is how my form looks like:
album/new.html.erb
<%= form_for ([#user, #album]), :html => { :id => "uploadform", :multipart => true } do |f| %>
<div class="formholder">
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.check_box :friends %>
<%= f.label :description %>
<%= f.text_area :description %>
<br>
<%=f.submit %>
</div>
<% end %>
an error occurs on line #6 (
<%= f.check_box :friends %>
the error:
undefined method 'friends' for #<Album:0x007fa3a4a8abc0>
I can understand why, but I don't know how to fix it. I have the typical friendship join model to add friends, and I want to be able to see a list of all the friends and select them. I think a following step would be to add something like #album.users << #user.friendships.find_by_name(params[:friends]) in the create action in the albums controller, but I don't know how I would loop through the form that only returns one param for friends?
Here are my files:
Albums controller create action:
def create
#user = User.find(params[:user_id])
#album = #user.albums.build(params[:album])
# not so sure about the following line.
#album.users << #user.friendships.find_by_name(params[:friends])
respond_to do |format|
if #user.save
format.html { redirect_to user_album_path(#user, #album), notice: 'Album was successfully created.' }
format.json { render json: #album, status: :created, location: #album}
else
format.html { render action: "new" }
format.json { render json: #album.errors, status: :unprocessable_entity }
end
end
end
album model
class Album < ActiveRecord::Base
attr_accessible :name, :description
validates_presence_of :name
has_many :album_users
has_many :users, :through => :album_user
has_many :photos
end
user model
class User < ActiveRecord::Base
has_secure_password
attr_accessible :email, :name, :password, :password_confirmation
validates_presence_of :password, :on => :create
validates_format_of :name, :with => /[A-Za-z]+/, :on => :create
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
validates_length_of :password, :minimum => 5, :on => :create
has_many :album_users
has_many :albums, :through => :album_users
accepts_nested_attributes_for :albums
has_many :friendships
has_many :friends, :through => :friendships
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
album_user model (join table to make many-to-many relationship between album, which has many users, and users, which has many albums)
class AlbumUser < ActiveRecord::Base
belongs_to :album
belongs_to :user
end
friendship model
class Friendship < ActiveRecord::Base
attr_accessible :friend_id
belongs_to :user
belongs_to :friend, :class_name => "User"
end
let me know if you need any more info!! Thanks in advance!!!
You should add users_ids (yes, two "s") to the list of accessible attributes of Album, and then use a "select multiple" on the :users_ids field.
<%= f.collection_select(:users_ids, User.all, :id, :name, :multiple => true) %>
I'm trying to create a new page called edit_profile for my User model so that the user can edit his profile(string). I'm following http://railscasts.com/episodes/41-conditional-validations
Here is the form (edit_profile.html.erb):
<%= form_for #user, :html => { :multipart => true } do |f| %>
<div class="field">
<%= f.label :profile %><br/>
<%= f.text_area :profile, :class => "round" %><br />
</div>
<div class="actions">
<%= submit_tag "update", :id => "updateSubmit" %>
</div>
<% end %>
The problem I'm having is that I have validation for the presence of password and password confirmation. When I load my edit_profile view, I keep getting this message Password is too short (minimum is 6 characters) even before I try to submit a new profile.
Here is my users_controller.rb:
def edit_profile
#user = current_user
#user.updating_password = false
#user.save
#title = "Edit profile"
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Account updated."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
How do I bypass my password validation when I just want to edit my profile attribute in the user model?
Thanks!
Other relevant information:
user.rb
class User < ActiveRecord::Base
attr_accessor :password, :updating_password
attr_accessible :name, :email, :password, :password_confirmation, :photo,
:profile
before_save :downcase_fields
before_save :encrypt_password
validates_presence_of :password, :if => :should_validate_password?
validates_confirmation_of :password, :if => :should_validate_password?
def should_validate_password?
updating_password || new_record?
end
validates :name, :presence => true,
:name_format => true,
:uniqueness => { :case_sensitive => false }
validates :email, :presence => true,
:email_format => true,
:uniqueness => { :case_sensitive => false }
validates :password,
#:presence => true,
#:confirmation => true,
:length => { :within => 6..40 }
validates :profile, :length => { :maximum => 160 }
end
Given you're talking about profile editing, i.e. you already have a registered user with password, you might want to conditionally skip password updating.
Just remove password and password_confirmation if password_confirmation is blank. I.e.:
class UsersController < ApplicationController
before_filter :skip_password_attribute, only: :update
...
def update
...
end
private
def skip_password_attribute
if params[:password].blank? && params[:password_validation].blank?
params.except!(:password, :password_validation)
end
end
end
(1) Typically, when #user is a new record, the form_for will go to create, and when the #user is not a new record it will go to update. If this fails to happen, then, you need to set the :url, an maybe the :method.
<%= form_for #user, :url => (#user.new_record? ? users_path : user_path(#user),
:html => (#user.new_record? ? { :multipart => true, :method => :post } : { :multipart => true, :method => :put } do |f| %>
(2) What you asked for is
class User
validate :validate_password_length
def validate_password_length
!new_record? || password.length >= 8
end
end
however, that lets a new user create an account the change the password to something shorter. So it would be better to do the following:
class User
validate :validate_password_length
def validate_password_length
!password_changed? || password.length >= 8
end
end
I recommend devise for user auth & stuff. This is taken from the devise source (https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb):
validates_presence_of :password, :if => :password_required?
validates_confirmation_of :password, :if => :password_required?
validates_length_of :password, :within => 6..128, :allow_blank => true
protected
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
Here's how I ended up solving this problem, though I'm pretty sure it's not ideal - the validation method would need to be more complex if you have additional password-strength rules.
Basically, you don't actually have to change the controller at all - you can fix up your model to handle this scenario.
First, you can set the :on => :create attribute on your password and password_confirmation fields. However, this has the side effect that users must supply a compliant password at registration, but can then change it to a non-compliant one later. To resolve this, I added a before_update callback to validate the password field when doing an update. My User class now looks something like this:
class User < ActiveRecord::Base
...
before_update :check_password
...
validates :password, presence: true,
length: { minimum: 6 },
on: :create
validates :password_confirmation, presence: true,
on: :create
...
private
...
def check_password
is_ok = self.password.nil? || self.password.empty? || self.password.length >= 6
self.errors[:password] << "Password is too short (minimum is 6 characters)" unless is_ok
is_ok # The callback returns a Boolean value indicating success; if it fails, the save is blocked
end
end
As noted in the comment above, the result of this method determines whether the save will be attempted. In order to prevent the user being dumped back into the edit form without an error message, I inject one. [:password] tells it which field to attach the error to.
You just need to add :on => :create to validate.
I would like to create pages for Discussions on a website, and on those discussion pages have users be able to write posts. The posts need to belong to the discussion and a user, and the discussion to a user.
I have thus created two models, two controllers, and one partial to put on the discussion show page. Note that the redirects from the controllers are just assigned to root_pages and others in no logical fashion, as I wanted to deal with redirects once I got the form working. I didn't attach the user model, as it is long and I didn't think it was necessary.
My problem is that I can't get the post controller to assign the correct discussion id to a new post. I'd like for this to be recorded, so that posts are associated to the author user_id (which works) and the discussion_id. I know that using #post.discussion_id = #discussion.id won't assign this properly, but I have tested #post.discussion_id = 1 to see if the rest of the code works (it does).
How do I change the set-up of the forms/controllers to assign the discussion_id here? Any help would be much appreciated!
Discussion Controller:
class DiscussionsController < ApplicationController
def show
#discussion = Discussion.find(params[:id])
#title = #discussion.title
#post = Post.new if signed_in?
end
end
Discussion Model:
class Discussion < ActiveRecord::Base
attr_accessible :title, :prompt
belongs_to :user
validates :title, :presence => true, :length => { :within => 5..100 }
validates :prompt, :presence => true, :length => { :within => 5..250 }
validates :user_id, :presence => true
has_many :posts, :dependent => :destroy
default_scope :order => 'discussions.created_at DESC'
end
Post Controller:
class PostsController < ApplicationController
def create
#post = current_user.posts.build(params[:post])
#post.discussion_id = #discussion.id
if #post.save
redirect_to discussion_path
else
redirect_to user_path
end
end
end
Post Model:
class Post < ActiveRecord::Base
attr_accessible :content
validates :content, :presence => true, :length => { :maximum => 10000 }
validates :user_id, :presence => true
validates :discussion_id, :presence => true
belongs_to :user
belongs_to :discussion
default_scope :order => 'posts.created_at ASC'
end
Partial for post form:
<%= form_for #post do |f| %>
<%= render 'shared/error_messages' %>
<div class="field">
<%= f.text_area :content, :class => "inputform largeinputform round" %>
</div>
<div class="actions">
<%= f.submit "Post", :class => "submitbutton round" %>
</div>
<% end %>
Your issue is that you do not have a mechanism to get the #discussion into the post controller One approach might be to put the discussion id in a hidden field in your partial form and then read it in your controller as a param.
Lance
in PostsController you're not creating a #discussion in create method.
I am new to Ruby and Rails so sorry if this looks too noob.
I have created a resource called stream and another resource called tasks and have mapped them properly using has_many and belong_to. Everything works until I decided to add a "Quick Task Add form" on my Stream.show view:
Here is the view code for the form:
<%= form_for(#task) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.text_field :title %> <%= f.submit "Add Task" %>
<%= hidden_field_tag(:stream_id, #stream.id) %>
</div>
<% end %>
Here is my Stream.show action:
def show
#stream = Stream.find(params[:id])
#user = User.find(#stream.user_id)
#tasks = #stream.tasks.paginate(:page => params[:page])
#title = #stream.title
#task = Task.new
end
And here is my task controller:
class TasksController < ApplicationController
def create
#stream = Stream.find(params[:stream_id])
#stream.tasks.create!({:title => params[:task][:title], :user_id => 1, :owner => 1})
if #stream.save
flash[:success] = "Task created succesfully!"
else
flash[:error] = "Error creating task"
end
redirect_to #stream
end
end
Looks pretty basic to me. The problem is when it executes tasks.create, I get the following error message: "Validation failed: User can't be blank, Owner can't be blank"
What am I doing wrong?
edit: adding model code from comment
class Stream < ActiveRecord::Base
attr_accessible :title
belongs_to :user
has_many :tasks, :dependent => :destroy
validates :title, :presence=> true, :length => { :maximum =>50 }
validates :user_id, :presence => true
end
class Task < ActiveRecord::Base
attr_accessible :title
belongs_to :stream
validates :title, :presence=> true, :length => { :maximum =>70 }
validates :user_id, :presence => true
validates :owner, :presence => true
validates :stream_id, :presence => true
default_scope :order => "updated_at"
end
You should set your user_id and owner fro STREAM object
class TasksController < ApplicationController
def create
#stream = Stream.find(params[:stream_id])
#stream.tasks.create!({:title => params[:task][:title], :user_id => 1, :owner => 1})
#stream.user_id = 1
#stream.owner = 1
if #stream.save
flash[:success] = "Task created succesfully!"
else
flash[:error] = "Error creating task"
end
redirect_to #stream
end
end
Unfortunately i can't test my suggestion currently but you might have to add
Attr_accessible :user,:owner
To the task model because you are mass-assigning these field using the hash.