I'm having this problem, I tried a lot of differents aproachs but
everytime it falls in that error.
Enviroment:
Rails 3.0.5
Mongoid 2.0.1
class User
include Mongoid::Document
field :name
has_and_belongs_to_many :companies
end
class Company
include Mongoid::Document
field :name
has_and_belongs_to_many :users
end
In my UserController method Create a I do something like this:
#user = User.where(:email => params[:user][:email])
if #user.count > 0
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
else
#user = User.create(:name => params[:user][:name],
:email => params[:user][:email],
:password => "123456")
#user.companies.push(#company)
#user.save
#company.users.push(#user)
#company.save
end
When the user dont exist works great.
But if the user is already in the DB, fall a error.
NoMethodError in UserController#create
undefined method `companies' for #<Array:0x10679f638>
But after all it pushes the object into the document.
I don't know if I'm missing something.
If someone know how to solve this ... will be great.
Thanks in advance.
Try this:
#user = User.where(:email => params[:user][:email]).first
On a side note, you may also want to push some of this code into one of your models, either the User or Company model, so that in your controller you would only have one call such as:
#company.add_user(#user)
The implementation details of adding a user would then be encapsulated in your model.
You may also want to consider embedding the two calls to ActiveRecord::Base#save into a single transaction to avoid ending up with inconsistent data in your database.
Related
I'm new to rails , and I have a problem with the nested forms and all of that.
I have a User model, and an Organization model.
When I want to create a user, I want to specify from which organization does he comes from.
Either the organization name is already in the database or if it's not, I want to create a new record and associate that record the User model.
I have hard time understanding all the relations (many-to-many etc) implications in the rails framework, but so far I've got this.
model/organization.rb
class Organization < ActiveRecord::Base
has_many :user
validates_presence_of :name
end
model/user.rb (short)
class User < ActiveRecord::Base
belongs_to :organization
accepts_nested_attributes_for :organization
#####
end
From this, in the console, I can create user and specify and organization name , and it will create a new record for the user and a new record for the organization.
The problem is that it creates a new organization each time.
I want to be able to associate an already existing organization to a new user.
I can get the list of organization with things like typeahead.js for the form, so the name will be the same when the user selects one. But I don't know how to relate the two (the newly created user and already existing organization).
I thought of putting a hidden field with the id of the organization, and check in the controller if this id exists. If it does, put this id, if it doesn't create a new one. But I don't even know how to do this. In the console, when I update the attributes of a user, for example , with an organization_id = 3 which exists :
u.update_attributes( :organization_attributes => { id: 3 } )
It rejects saying he didn't find a user with ID=... with Organization.id = 3 ...
I don't understand.
I suppose since this is a common case, that this should be easy , but it's messing with my head.
If someone is willing to explain to me, I'd be very grateful.
Thank you.
EDIT
i've just tried something in my controller but that doesn't work either.
def create
#user = User.new(user_params) # :user object built from user inputform
org = Organization.find_by(name:user_params[:organization_attributes][:name])
if org
#user.organization.id = org.id
end
if #user.save
# signin the user (token etc)
sign_in #user
flash[:success] = "Registration sucessfull !"
redirect_to #user
else
render 'new'
end
end
+user_controller (strong params)
def user_params
params.require(:user).permit(:lname,:email,:fname,:password,:password_confirmation,
:gender,:role,:display_private,:link_li,:country,:city,:phone,:hobbies,
:avatar,:org_name, :organization_attributes => [ :id, :name])
end
+form.html.erb
<%= u.fields_for :organization do |o| %>
<%= o.label "Organization" %>
<!-- PUT ORGA -->
<%= o.text_field :name, class:"form-control" %>
<% end %>
I would write a custom method for this:
#in User
def organization_name
(org = self.organization) && org.name
end
def organization_name=(name)
if org = Organization.find_by_name(name)
self.organization = org
else
self.organization = Organization.create(:name => name)
end
end
Now, because you've got a getter and setter method (ie two methods with the same name, apart from the = sign), you can treat organization_name like an attribute of User and put it in a form field like
f.input :organization_name
The input will get the current value from #user.organization_name and will call #user.organization_name= with the new value.
First take away the accepts_nested_attributes from the model.
Then in your controller you should do something like:
def create
#user = User.new(user_params) # :user object built from user inputform
org = Organization.where(name: user_params[:organization_attributes][:name]).first || Organization.create(name: user_params[:organization_attributes][:name])
#user.organization = org
if #user.save
# signin the user (token etc)
sign_in #user
flash[:success] = "Registration sucessfull !"
redirect_to #user
else
render 'new'
end
end
In your app/model/user.rb
def self.create(name, attribute1, ... ,organization)
user = User.new(:name => name, :atr_1 => attribute_1, ....:atr_n => attribute_n)
user.organization = organization
raise "user not created" if !user.save
user
end
In users_controller.rb
def create
org = Organization.find params['organization'] #expecting the param to be Organization#id
user = User.create(params['name'], ..., org)
render :json => {:message => "user created"}
end
So this has been asked previously, but with no satisfying answers.
Consider two models, User, and Subscription associated as such:
class User < ActiveRecord::Base
has_one :subscription, dependent: :destroy
end
class Subscription < ActiveRecord::Base
belongs_to :user
end
Inside of SubscriptionsController, I have a new action that looks like this
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
Given that a subscription already exists for a user record, I'm faced with the following problem:
user.build_subscription is destructive, meaning that simply visiting the new action actually destroys the association, thereby losing the current subscription record.
Now, I could simply check for the subscription's existence and redirect like this:
def new
user = User.find(params[:user_id])
if user.subscription.present?
redirect_to root_path
else
#subscription = user.build_subscription
end
end
But that doesn't seem all that elegant.
Here's my question
Shouldn't just building a tentative record for an association not be destructive?
Doesn't that violate RESTful routing, since new is accessed with a GET request, which should not modify the record?
Or perhaps I'm doing something wrong. Should I be building the record differently? Maybe via Subscription.new(user_id: user.id)? Doesn't seem to make much sense.
Would much appreciate an explanation as to why this is implemented this way and how you'd go about dealing with this.
Thanks!
It depends on what you want to do
Thoughts
From what you've posted, it seems the RESTful structure is still valid for you. You're calling the new action on the subscriptions controller, which, by definition, means you're making a new subscription (not loading a current subscription)?
You have to remember that Rails is basically just a group of Ruby classes, with instance methods. This means that you don't need to keep entirely to the RESTful structure if it doesn't suit
I think your issue is how you're handling the request / action:
def new
user = User.find(params[:user_id])
#subscription = user.build_subscription
end
#subscription is building a new ActiveRecord object, but doesn't need to be that way. You presumably want to change the subscription (if they have one), or create an association if they don't
Logic
Perhaps you could include some logic in an instance method:
#app/models/user.rb
Class User < ActiveRecord::Base
def build
if subscription
subscription
else
build_subscription
end
end
end
#app/controllers/subscriptions_controller.rb
def new
user = User.find(params[:user_id])
#subscription = user.build
end
This will give you a populated ActiveRecord, either with data from the subscription, or the new ActiveRecord object.
View
In the view, you can then use a select box like this:
#app/views/subscriptions/new.html.erb
<%= form_for #subscription do |f| %>
<%= "User #{params[:user_id]}'s subscription: %>
<%= f.collection_select :subscription_id, Subscription.all,:id , :name %>
<% end %>
They are my thoughts, but I think you want to do something else with your code. If you give me some comments on this answer, we can fix it accordingly!
I also always thought, that a user.build_foobar would only be written to the db, if afterwards a user.save is called. One question: After calling user.build_subscription, is the old subscription still in the database?
What is the output user.persisted? and user.subscription.persisted?, after calling user.build_subscription?
Your method to check if a subscription is present, is IMHO absolutely ok and valid.
I came across this today and agree that deleting something from the db when you call build is a very unexpected outcome (caused us to have bad data). As you suggested, you can work around if very easily by simply doing Subscription.new(user: user). I personally don't think that is much less readable then user.build_subscription.
As of 2018 Richard Peck's solution worked for me:
#app/models/user.rb
Class User < ActiveRecord::Base
def build_a_subscription
if subscription
subscription
else
build_subscription
end
end
end
My issue was that a user controller didn't have a new method, because users came from an api or from a seed file.
So mine looked like:
#app/controllers/subscriptions_controller.rb
def update
#user = User.find(params[:id])
#user.build_a_subscription
if #user.update_attributes(user_params)
redirect_to edit_user_path(#user), notice: 'User was successfully updated.'
else
render :edit
end
end
And I was finally able to have the correct singular version of subscriptions in my fields_for, so :subscription verses :subscriptions
#app/views
<%= f.fields_for :subscription do |sub| %>
<%= render 'subscription', f: sub %>
<% end %>
Before I could only get the fields_for to show in the view if I made subscriptions plural. And then it wouldn't save.
But now, everything works.
I want to retrieve an entire user object via a get request. Currently, on of my methods looks like this:
def user
#user = User.find(current_user.id)
render :json => #user
end
This returns the following json:
"{\"annonymous_token\":null,\"email\":\"john1#doe.com\",\"name\":\"john1#doe.com\"}"
My user object / schema has a many-to-many relationship with another model called pictures.
has_many :picturization
has_many :pictures, :through => :picturization
How can I modify my code such that returning the user also returns the pictures the user has. If helpful, I am using devise for user authentication.
Thanks!
You can use .as_json and pass params as I show here: https://stackoverflow.com/a/11336145/308701
so you could do:
def user
#user = User.find(current_user.id)
render :json => #user.as_json(:include => :pictures)
end
Have you tried #user.to_json?
i have a problem in ruby on rails. I want to make current user's store id to be 0 when user accesses to /homepage/, and i want to make user's store id to be the input id in the url when user accesses to /homepage/:id/.
My code:
routes.rb:
match "/homepage" => "users#access", :as => "store"
match "/homepage/:id" => "users#homepage", :as => "store"
def access
#user = current_user
#user.update_attributes(:store => "0")
#user.save
end
def homepagestore
#user = current_user
#user.update_attribute(:id, user.store = :id)
#user.save
end
update_attribute updates the record in the database. But it skips the validation checks. update_attributes also updates (saves) the record in the database. It does not skip validation.
So:
You should use params[:id] as Sergio says
You may want to use update_attributes instead since it does not skip validation checks
You do NOT need the save method if you use update_attribute or update_attributes
My suggestions:
def access
#user = current_user
#user.update_attributes(:store => "0")
end
def homepagestore
#user = current_user
#user.update_attributes(:store => params[:id])
end
Added update_attributes uses the mass-assignment protection system. So you need the :store field in your User model's attr_accessible call to allow it to be changed. Or override the protection, see the update_attributes docs. Ask if you have more questions.
I have a Rails app with a user model that contains an admin attribute. It's locked down using attr_accessible. My model looks like this:
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation
attr_accessible :name, :email, :other_email, :plant_id, :password, :password_confirmation, :admin, :as => :admin
And here's what my update method in my users controller looks like:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user], :as => current_user_role.to_sym)
flash[:notice] = "Profile updated"
redirect_to edit_user_url(#user)
else
render 'edit'
end
end
I have a helper method in my application controller that passes back the role as a string:
def current_user_role
#current_user_role ||= current_user.admin? ? "admin" : "default"
end
helper_method :current_user_role
I've also set config.active_record.whitelist_attributes = true in config/application.rb.
I've verified that the current_user_role method is returning the proper value based on the current user's admin status. Rails isn't throwing a mass-assignment error. But when I try to update a user's admin status while logged in as an admin, Rails performs the update and silently ignores the admin attribute. Pulling up the user's record in the Rails console shows that the record hasn't been modified.
I have a feeling there's a Ruby- or Rails-specific issue at play that I'm not aware of. I can't locate any info on making the role dynamic. The best I could find was this.
There was an errant attr_accessor :admin in my model that was left in from a prior attempt at getting this to work. I overlooked it. Removing it fixed it.
So, the upshot is that this is a pretty simple way to get dynamic roles working in Rails 3.2.
Looks like it could be a bug in Rails 3.2
https://github.com/stffn/declarative_authorization/issues/127