Rails - belongs_to association not updating foreign key - activerecord

I have the following:
class Publication < ActiveRecord::Base
belongs_to :project, :inverse_of => :publication
before_create :bind_project
private
def bind_project
self.create_project
end
end
class Project < ActiveRecord::Base
has_one :publication, :inverse_of => :project
end
According to this when creating a new Publication the publication_id attribute on the project model should be set by the create_project method.
Why it does not happen?
This is what I see on bind_project:
self.project_id is set correctly
self.project.publication_id is NULL
self.project.publication.id is set correctly
The database reflects this also: the projects.publication_id column is NULL.

Seems a bit strange, that you try to access the create_project method in a before_create callback. Typo? An after_create callback seems to be more appropriate.
Moreover: What for do you need the publication_id attribute on the has_one side of an association? There only needs to be one _id attribute on the belongs_to side.
Addendum to my first paragraph: As I see it, you are trying to use Rails magic on the associated object (the create_project method) before the actual base object is finished being created. Although this might work, this would be my first point to investigate.

Related

Papertrail tracks create but not delete through association

I have a table of Industries and am keeping tracking of it's competitors which are also industries. This is through a mapping table industry_competitors which has industry_id and competitor_id. I want papertrail to track associations and dissociations of industry competitors.
class Industry < ApplicationRecord
has_many :industry_competitors, dependent: :destroy
has_many :competitors, through: :industry_competitors
end
class IndustryCompetitor < ApplicationRecord
has_paper_trail
belongs_to :industry
belongs_to :competitor, class_name: "Industry"
end
My controller code is as such.
competitors = ::Industry.where(id: params[:competitor_ids])
#industry.competitors = competitors
#industry.save
Every time the entire competitor list is passed. If I try to disassociate a few competitor (by not passing the ids to the controller) from the industry a 'Delete' query is fired.
DELETE FROM `industry_competitors` WHERE `industry_competitors`.`industry_id` = 4559 AND `industry_competitors`.`competitor_id` = 4564
I suspect because activerecord calls 'delete' and not 'destroy' this papertrail callbacks are not triggered hence the changes are not tracked.
If there a way to call delete explicitly (with minimal code changes). Or is there a way for papertrail to track delete?
Adding this patch can get it to work.
module HasManyThroughAssociationPatch
def delete_records(records, method)
method ||= :destroy
super
end
end
ActiveRecord::Associations::HasManyThroughAssociation.prepend(HasManyThroughAssociationPatch)
Credits: https://github.com/westonganger/paper_trail-association_tracking

Defining functionally identical models using similar databases in Rails 3.2

I've got a database with an old schema that I'm going to be migrating data from. The table names and relationships are identical to the database. For some reason when I define the models for the old instance, they are acting as though they're the models of the new database.
from_db.rb
class FromDB < ActiveRecord::Base
self.abstract_class = true
establish_connection FROM_DB
end
from_clip.rb
class FromClip < FromDB
self.table_name = "clips"
belongs_to :clippable, polymorphic: true, counter_cache: true
belongs_to :video, class_name: "FromVideo"
end
clip.rb
class Clip < ActiveRecord::Base
belongs_to :clippable, polymorphic: true, counter_cache: true
belongs_to :video
end
console
FromClip.first.class.name
FromClip Load (0.2ms) SELECT `clips`.* FROM `clips` LIMIT 1
=> "Clip"
All the methods and associations available to FromClip are those in the Clip model rather than the FromClip model, but the instance of FromClip is from the correct database. Thanks for your help
So, it was related to STI. There are two models that inherit from Clip. If I use
self.inheritance_column = :_non_existing_column
in the FromClip model, it works as expected. I just have to point the clippable method at the right models and make sure I bring over the type column correctly.
def clippable
("From" + clippable_type).constantize.find_by_id(clippable_id)
end

Attempting to create a database item using the has_one relationship, no exceptions, but still no item

Models:
A User has_one Ucellar
A Ucellar belongs_to User
I have confirmed from multiple sources that these are set up correctly. For posterity, here is the top portion of those two models.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
This is actually the entire Ucellar model.
class Ucellar < ActiveRecord::Base
belongs_to :user
end
Ucellar has a column called user_id, which I know is necessary. The part of my application that creates a user uses the method create_with_oath. Below is the entire User class. Note the second line of the create method.
class User < ActiveRecord::Base
has_many :authorizations
has_one :ucellar
validates :name, :email, :presence => true
def create
#user = User.new(user_params)
#ucellar = #user.create_ucellar
end
def add_provider(auth_hash)
# Check if the provider already exists, so we don't add it twice unless authorizations.find_by_provider_and_uid(auth_hash["provider"], auth_hash["uid"])
Authorization.create :user => self, :provider => auth_hash["provider"], :uid => auth_hash["uid"]
end
end
def self.create_with_omniauth(auth)
user = User.create({:name => auth["info"]["name"], :email => auth["info"]["email"]})
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
EDIT:
Forgot to summarize the symptoms. On create, the user is in the db, with no exceptions thrown, and nothing to signify that anything went wrong. However, the related ucellar is never created. Per the documentation Here, the create method should create AND save the related ucellar.
It should create ucellar too.
Try to get the error messages after the creation by calling:
raise #user.errors.full_messages.to_sentence.inspect
I'm not sure why this wasn't working, but I ended up just moving this code out of the create action of the user controller, and putting it directly after an action that was creating a user. It solved my issue though. Thanks everyone for your help!

Rails nested form on many-to-many: how to prevent duplicates?

I've setup a nested form in my rails 3.2.3 app, it's working fine, my models are:
class Recipe < ActiveRecord::Base
attr_accessible :title, :description, :excerpt, :date, :ingredient_lines_attributes
has_and_belongs_to_many :ingredient_lines
accepts_nested_attributes_for :ingredient_lines
end
and:
class IngredientLine < ActiveRecord::Base
attr_accessible :ingredient_id, :measurement_unit_id, :quantity
has_and_belongs_to_many :recipes
belongs_to :measurement_unit
belongs_to :ingredient
end
As above, a Recipe can have multiple IngredientLines and vice versa.
What I'm trying to avoid is record duplication on IngredienLine table.
For example imagine that for recipe_1 an IngredientLine with {"measurement_unit_id" => 1, "ingredient_id" => 1, "quantity" => 3.5} is associated, if for recipe_5 the IngredientLine child form is compiled by the user with the same values, I don't want a new record on IngredientLine table, but only a new association record in the join table ingredient_lines_recipes.
Note that currently I dont't have any IngredientLine controller as saving and updating IngredientLines is handled by nested form routines. Even my Recipe controller is plain and standard:
class RecipesController < ApplicationController
respond_to :html
def new
#recipe = Recipe.new
end
def create
#recipe = Recipe.new(params[:recipe])
flash[:notice] = 'Recipe saved.' if #recipe.save
respond_with(#recipe)
end
def destroy
#recipe = Recipe.find(params[:id])
#recipe.destroy
respond_with(:recipes)
end
def edit
respond_with(#recipe = Recipe.find(params[:id]))
end
def update
#recipe = Recipe.find(params[:id])
flash[:notice] = 'Recipe updated.' if #recipe.update_attributes(params[:recipe])
respond_with(#recipe)
end
end
My guess is that should be enough to override the standard create behavior for IngredientLine with find_or_create, but I don't know how to achieve it.
But there's another important point to take care, imagine the edit of a child form where some IngredientLines are present, if I add another IngredientLine, which is already stored in IngredientLine table, rails of course should not write anything on IngredientLine table, but should also distinguish between child records already associated to the parent, and the new child record for which needs to create the relation, writing a new record on the join table.
Thanks!
in Recipe model redefine method
def ingredient_lines_attributes=(attributes)
self.ingredient_lines << IngredientLine.where(attributes).first_or_initialize
end
Old question but I had the same problem. Forgot to add :id to white list with rails 4 strong_parameters.
For example:
widgets_controller.rb
def widget_params
params.require(:widget).permit(:name, :foos_attributes => [:id, :name, :_destroy],)
end
widget.rb
class Widget < ActiveRecord::Base
has_many :foos, dependent: :destroy
accepts_nested_attributes_for :foos, allow_destroy: true
end
foo.rb
class Foo < ActiveRecord::Base
belongs_to :widget
end
I have run into a similar situation and found inspiration in this answer. In short, I don't worry about the duplication of nested models until save time.
Translated to your example, I added autosave_associated_records_for_ingredient_lines to Recipe. It iterates through ingredient_lines and performs a find_or_create as your intuition said. If ingredient_lines are complex, Yuri's first_or_initialize approach may be cleaner.
I believe this has the behavior you're looking for: nested models are never duplicated, but editing one causes a new record rather than updating a shared one. There is the strong possibility of orphaned ingredient_lines but if that's a serious concern you could choose to update if that model has only one recipe with an id that matches the current one.

Rails 3 relationship definition problem?

I'm new to Rails 3, i'm creating a web app that use active admin, i get a problem with him, and asked for help inside github plugin, someone told me maybe a relationship definitions.
I really dont know what is that, i have nested elements and in active admin i want to make nested element independent.
but now, im totally lost. what i missed? thanks.
here is my model definition
class Company < ActiveRecord::Base
before_save :getsubdomain
has_attached_file :logo, :styles => { :thumb => '150x150>', :medium => '250x250>', :normal => '350x350>'}
has_many :buildings
accepts_nested_attributes_for :buildings
end
Building model
class Building < ActiveRecord::Base
belongs_to :companies
end
in my db, i have colum company_id in buildings table.
Here the error message i get..
NameError in Admin/buildings#index
Showing /Library/Ruby/Gems/1.8/bundler/gems/active_admin-c3a1ffa98072/app/views/active_admin/resource/index.html.arb where line #1 raised:
uninitialized constant Building::Companies
Rails.root: /Users/username/Sites/myapps
Request
Parameters:
{"order"=>"id_desc"}
Response
Headers:
None
thanks for your help
belongs_to expects a singular name. Try
belongs_to :company

Resources