I have 2 model one for user(amitians) other is about.rb to store their details
they have a has_one relationship between them but problem is whenever I create a new about my about table has an amitan_id = null
When I tried to do the same in rails console. It works fine.
here is my code for models
About_amitians.rb
class AboutAmitian < ApplicationRecord
belongs_to :amitian
end
Amitian.rb
has_one :about_amitian , foreign_key: "amitian_id"
My AboutAmitian controller
def new
#about_amitian = current_amitian.build_about_amitian
end
def create
#about_amitian = current_amitian.create_about_amitian!(about_amitian_params)
if #about_amitian.save
redirect_to :back
flash[:notice] = 'success'
else
render root_url
end
end
private
def about_amitian_params
params.require(:about_amitian).permit(:dob,:interest,:bio,:catch_phrase,:relationship_status)
end
In my server logs I have this query
Update 'about_amitians' set 'amitian_id' = NULL where 'about_amitian'.'id' = 1
andhere are the params send via form
Parameters: {"utf8"=>"✓", "authenticity_token"=>"6MtQlOfl4kU1BPMDT81m8rwwxSYdaQKpUEZbvnFw9ux1nVELSTSmaRNOgUCKNyTe2VrrkY01Ewn70hGWn/9wQg==", "about_amitian"=>{"dob"=>"m", "interest"=>"vm", "bio"=>"blyhjb", "catch_phrase"=>"hjkl", "relationship_status"=>"bljhbl"}, "commit"=>"Create About amitian"}
I finally solved it but this is insane...
Everything is fine in the code.. problem is completely diff than what I assumed.
Whenever I submit the form everything is fyn and i have my amitian_id set correctly. But whenever I redirect to my new page... my amitian_id updates to NULL.
As you can see from code.. I redirect_to :back (i.e back to new) so my amitian_id updates to null.
Related
I want to create a record in join table but rails shows me two errors in two situation, and I don't want to generate a third model.
#channel = Channel.find(params[:channel_id])
if #channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
flash[:success] = "U Succeed:)"
redirect_to request.referrer
else
flash[:danger] = "U Nit Succeed:H"
redirect_to request.referrer
end
second situation
if Channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
flash[:success] = "U Succeed:)"
redirect_to request.referrer
else
flash[:danger] = "U'r Not Succeed:H"
redirect_to request.referrer
end
I want to save attrs in join table. According to rails official site guide, what's wrong?
First error:
unknown attribute 'channel_id' for User.
Second error:
undefined method `users' for Class:0x00007feaa0312058
I am assuming that you have associations like these:
class User < ActiveRecord::Base
has_and_belongs_to_many :channels
end
class Channel < ActiveRecord::Base
has_and_belongs_to_many :users
end
Now you are trying to do like this:
#channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
This will try to create a new User class object as there is no Model in between you just have a mid table. Instead you can do it like this:
# If you don't have the user object already
user = User.find params[:user_id]
# This will create a record in the mid table
#channel.users << user
This will create a new record in the mid table and the existing records will also exist as it is. And if you do like this:
#channel.users = user
This will delete all the existing associated user records from the mid table for this channel and add a new associated record with this user.
And when you try doing like this:
Channel.users.create!(channel_id: params[:channel_id], user_id: params[:user_id])
This is not valid at all because the class Channel doesn't have any direct relation with the User but an instance of Channel class may be associated with instances of User class.
For the first scenario i would suggest you should do it like
#channel.user_ids = #channel.user_ids + [params[:user_id]]
it will create join table records, you can surely try optimised approach for this as you see fit.
you can use push or << method instead of create : Channel.users.push(attrs) or Channel.users << (attrs) and second answer in good too but .ids not very readable
or you can find channel by id and use it : channel.users.create(attrs)
see api.rubyonrails.org and search has_and_belongs_to_many methods in searchbar
I was writing test for updating images in my rails application. The pictures model in my app is a polymorphic association and it belongs to more than one model. I am testing the create, update and destroy action workflow in the integration tests. I have tested the create action successfully. Its working fine. The problem I am having is with the update action.
The model association with the pictures model is given below.
class Picture < ActiveRecord::Base
belongs_to :pictureable, polymorphic: true
# each user has one picture
has_one :picture, as: :pictureable
#each scoreboard has one picture
has_one :picture, as: :pictureable, dependent: :destroy
The code for the picture_update_test is given below.
def setup
#picture = pictures(:picture_a) #this is the picture associated with pictureable (scoreboard_a)
#scoreboard = scoreboards(:scoreboard_a) #pictureable is scoreboard_a
end
test "successful update where pictureable is scorebaord" do
patch scoreboard_picture_path(#scoreboard, #picture), picture: {picture: "blank-prof.jpg"}
end
The code in the picture.yml file is given below.
picture_a:
picture: "blank-prof.jpg"
pictureable_type: scoreboard
Once I run the tests, I get the following error.
NoMethodError: undefined method `update_attributes' for nil:NilClass
app/controllers/pictures_controller.rb:19:in `update'
The picture controller code is also given below.
def update
#picture = #pictureable.picture
if #picture.update_attributes(picture_params)
redirect_to #pictureable
flash[:success] = "Picture updated successfully"
else
redirect_to #pictureable
flash[:danger] = "An error occured, please try again!"
end
end
I find out if #pictureable is a user or a scoreboard by splitting the url. The code is given below.
def load_pictureable
resource, id = request.path.split('/')[1,2]
#pictureable = resource.singularize.classify.constantize.find(id)
end
I run a before_filter for that method. before_filter :load_pictureable.
In the tests, I don't need to split any url. I am specifically stating the url path, scoreboard_picture_path or 'user_picture_path'. I just have to pass the scoreboard_id and the picture.id. The error states update_attributes for nil class. I am really not sure why I am getting this error. I am not sure if pictures.yml file is correct in terms of the association. As always, any help would be greatly appreciated. Thanks!!
In your yaml you're setting pictureable_type but not picturable_id and so the association is not being built correctly.
You can see the fixtures docs for examples of setting up polymorphic association fixtures, but basically instead of what you're doing you should be able to do something like:
picture_a:
picture: "blank-prof.jpg"
pictureable: scoreboard_a (Scoreboard)
That should then allow #pictureable.picture to return the picture.
I'm working in Rails and I have two models, a prelaunch and an initiative. Basically I want a user to be able to create an initiative using the attributes of the prelaunch. Basically what I want to have happen is when a user visit's their prelaunch and is ready to turn it into an initiative, it brings them to a form that has their prelaunch information already populated and they can just add the additional info. I've managed to do this for every attribute so far except for the attached image, called :cover_image.
I think the problem is that I'm setting the initiative's cover_image to the prelaunch's cover_image on the new action of my controller, but because this is the new action and not create, I'm not saving the initiative yet. I think this means the cover_image isn't getting reuploaded yet, so #iniative.cover_image.url doesn't point to anything. It also doesn't appear to be prepopulating the file field of my form with anything.
I'm not entirely sure how feasible all of this is, but it's what the client asked for so I'm trying to make it work for them.
Here's my controller:
def new
#initiative = Initiative.new
populate_defaults(#initiative)
#initiative.build_location
3.times{ #initiative.rewards.build }
#initiative.user = current_user
if !params[:prelaunch_id].nil? && !params[:prelaunch_id].empty?
# if user is transferring a prelaunch, assign its attributes to the intiative
#prelaunch = Prelaunch.find(params[:prelaunch_id])
#initiative.assign_attributes(title: #prelaunch.title,
teaser: #prelaunch.teaser,
category: #prelaunch.category,
funding_goal: #prelaunch.funding_goal,
term: #prelaunch.campaign.term,
story: #prelaunch.story,
location: #prelaunch.campaign.location,
video_url: #prelaunch.video_url,
EIN: #prelaunch.campaign.EIN,
nonprofit: #prelaunch.nonprofit,
organization_name: #prelaunch.campaign.organization.name)
end
end
Edit:
Thanks to peterept's answer below I've managed to get the prelaunch cover_image into the form and into the create action of the initiatives controller. The problem now is that everything seems to work perfectly in the create action: the initiative gets the prelaunch's cover image, it saves without error, and it redirects to the show action.
UNFORTUNATELY, By the time it reaches the show action of the controller, #initiative.cover_image is set to the default again. I can't figure out what could possibly be happening between the successful create action and the show action.
Here are the create and show actions of the initiatives controller:
def create
if !params[:initiative][:prelaunch_id].nil? && !params[:initiative][:prelaunch_id].empty?
#prelaunch = Prelaunch.find(params[:initiative][:prelaunch_id]) # find the prelaunch if it exists
end
#initiative = Initiative.new(initiatives_params)
#initiative.user = current_user
begin
#payment_processor.create_account(#initiative)
if #initiative.save
# #prelaunch.destroy # destroy the prelaunch now that the user has created an initiative
flash[:alert] = "Your initiative will not be submitted until you review the initiative and then press 'Go Live' on the initiative page"
redirect_to initiative_path(#initiative)
else
flash[:alert] = "Initiative could not be saved: " + #initiative.errors.messages.to_s
render :new
end
rescue Exception => e
logger.error e.message
flash[:error] = "Unable to process request - #{e.message}"
render :new
end
end
def show
#initiative = Initiative.find(params[:id])
#other_initiatives = Initiative.approved.limit(3)
end
And here is the initiatives_params method from the same controller:
def initiatives_params
initiative_params = params.require(:initiative).permit(
:terms_accepted,
:title,
:teaser,
:term,
:category,
:funding_goal,
:funding_type,
:video_url,
:story,
:cover_image,
:nonprofit,
:EIN,
:role,
:send_receipt,
:organization_name,
:crop_x, :crop_y, :crop_h, :crop_w,
location_attributes: [:address],
rewards_attributes: [:id, :name, :description, :donation, :arrival_time, :availability, :_destroy, :estimated_value])
if #prelaunch.media.cover_image
initiative_params[:cover_image] = #prelaunch.media.cover_image
end
initiative_params
end
You can pass the Image URL and display it on the page.
The user can then override this by uploading a new image (as per normal).
In you're create action, if they have not supplied a new image, then set it to the one in the assocoiated prelaunch - you'd want to copy the original so it doesn't get replaced if they upload a new one. (If you don't know which was the prelaunch, you could pass the ID down to the page).
I was able to make it work by saving the Paperclip object only. This is my model:
class Grade < ActiveRecord::Base
has_attached_file :certificate
end
If I run the following:
#grade.certificate = new_file
#grade.certificate.save
It saves/overwrite the file, but don't update the Grade object.
Versions: ruby-2.3.8, Rails 4.2.11.3 and paperclip (4.3.6)
Update
I had to change the piece of the code below to this for it to work properly:
attrs_to_check.each do |attr|
if self.send("#{attr}_changed?") && ( !self.send("#{attr}_was").nil? && !self.send(attr).nil? )
self.last_updated_hash[attr] = DateTime.now
end
end
But I'd still like to understand why I need to add the extra if statement logic. It seems like it should be unnecessary.
Original
I am adding a serialized hash to my model to record the datetime that certain fields in the model were last updated. The strange thing is that this works fine except for on the first update action, where it seems to treat everything as dirty regardless if changes were made to those particular attributes. And what seems even more strange, it treats it as dirty even if there weren't any changes made to any of the attributes (just click the submit button on the edit form without making a change). Why might that be?
Here's the code for the model:
class ResearchCompany < ActiveRecord::Base
attr_accessible :name, :revenue_stats, :customer_qty_stats, :last_updated_hash
serialize :last_updated_hash, Hash
before_save :set_last_updated_hash
def set_last_updated_hash
attrs_to_check = ['revenue_stats', 'customer_qty_stats']
attrs_to_check.each do |attr|
if self.send("#{attr}_changed?")
self.last_updated_hash[attr] = DateTime.now
end
end
end
end
And the relevant parts of the controller:
class Admin::ResearchCompaniesController < ApplicationController
def create
#company = ResearchCompany.new(params[:research_company])
if #company.save
redirect_to admin_research_companies_path
else
render 'new'
end
end
def update
#company = ResearchCompany.find(params[:id])
if #company.update_attributes(params[:research_company])
redirect_to admin_research_companies_path
else
render 'edit'
end
end
end
When the record is first created:
self.last_updated_hash = {}
But when the record is first updated, even if the revenue_stats or customer_qty_stats attributes were not changed:
self.last_updated_hash = { "revenue_stats"=>Sat, 19 Oct 2013 11:29:48 -0400, "customer_qty_stats"=>Sat, 19 Oct 2013 11:29:48 -0400 }
But from that point forward, the datetime stored in the hash is only updated when one of those individual attributes changes. That is the proper behavior.
Any ideas? Thanks.
I have a model named Post and I created two methods within the model that make changes to fields. The first method's changes get persisted when a save is called. The second method's changes do not get saved. I have noticed this behavior before in other models and I think I'm missing some basic knowledge on how models work. Any help on this would be greatly appreciated!
class Post < ActiveRecord::Base
def publish(user) # These changes get saved
reviewed_by = user
touch(:reviewed_at)
active = true
end
def unpublish() # These changes get ignored.
reviewed_by = nil
reviewed_at = nil
active = false
end
end
EDIT:
Here is a snippet from the controller"
class PostsController < ApplicationController
def publish
if request.post?
post = Post.find(params[:id].to_i)
post.publish(current_user)
redirect_to(post, :notice => 'Post was successfully published.')
end
end
def unpublish
if request.post?
post = Post.find(params[:id].to_i)
post.unpublish()
redirect_to(post, :notice => 'Post was successfully unpublished.')
end
end
...
UPDATE
Problem was solved by adding self to all the attributes being changed in the model. Thanks Simone Carletti
In publish you call the method touch that saves the changes to the database. In unpublish, you don't save anything to the database.
If you want to update a model, be sure to use a method that saves the changes to the database.
def publish(user)
self.reviewed_by = user
self.active = true
self.reviewed_at = Time.now
save!
end
def unpublish
self.reviewed_by = nil
self.reviewed_at = nil
self.active = false
save!
end
Also, make sure to use self.attribute when you set a value, otherwise the attribute will be consideres as a local variable.
In my experience you don't persist your changes until you save them so you can
explicitly call Model.save in your controller
explicitly call Model.update_attributes(params[:model_attr]) in your controller
if you want to save an attribute in your model I saw something like write_attribute :attr_name, value but TBH I never used it.
Cheers