Search code examples
ruby-on-railsjoinnomethoderrortable-relationships

Method missing when calling an method associated with another class (:belongs_to relationship)


We've attempted a has_many, belongs_to relationship: we've created a class - ArticleCategory - that belongs to Article. Articles have many article_categories. ArticleCategory has one attribute - sport:string

We are unable to call Article.last.article_categories as it returns this error message:

NoMethodError: undefined method

Here is our relevant code:

ArticleCategories Controller

class ArticleCategoriesController < ApplicationController
  before_action :set_article_category, only: [:show, :edit, :update, :destroy]

  def index
    @article_categories = ArticleCategory.all
    respond_with(@article_categories)
  end

  def show
    respond_with(@article_category)
  end

  def new
    @article_category = ArticleCategory.new
    respond_with(@article_category)
  end

  def edit
  end

  def create
    @article = Article.find(params[:article_id])
    @article_category = @article.article_categories.build(article_category_params)



    # @article_category = ArticleCategory.new(article_category_params)
    @article_category.save
    respond_with(@article_category)
  end

  def update
    @article_category.update(article_category_params)
    respond_with(@article_category)
  end

  def destroy
    @article_category.destroy
    respond_with(@article_category)
  end

  private
    def set_article_category
      @article_category = ArticleCategory.find(params[:id])
    end

    def article_category_params
      params.require(:article_category).permit(:sport)
    end
end

ArticleCategories Model

class ArticleCategory < ActiveRecord::Base
    belongs_to :article
end

Articles Controller

class ArticlesController < ApplicationController

  load_and_authorize_resource
  skip_authorize_resource :only => [:index, :show]

def new
    @article = Article.new
end

def create
    @article = Article.new(article_params)
#  authorize! :create, @article

    if @article.save
    #send email to referral email
    all_users = User.all
    all_users.each do |user|
      ArticleMailer.article_confirmation(user,@article).deliver
   end 
   redirect_to @article
    else
     render 'new'
    end
end

def show
    @article = Article.find(params[:id])
end

def index
  @articles = Article.all.reverse
end

def edit
    @article = Article.find(params[:id])
end

def update
  @article = Article.find(params[:id])

  if @article.update(article_params)
    redirect_to @article
  else
    render 'edit'
  end
end

def destroy
  @article = Article.find(params[:id])
  @article.destroy

  redirect_to articles_path
end

private
  def article_params
    params.require(:article).permit(:title, :text, :date, :kredit, article_categories_attributes: [:id, :sport])
  end

end

Articles Model

class Article < ActiveRecord::Base
    has_many :comments, dependent: :destroy
    has_many :article_categories, dependent: :destroy
    accepts_nested_attributes_for :article_categories, :allow_destroy => true
  validates :title, presence: true,
                    length: { minimum: 5 }


end

We can't figure out why we can't call this method on Article.last


Solution

  • Since my comment helped, I'll add this as an answer so we can close this off

    If you had the console open when you made changes to the model, those changes aren't reflected yet until you either exit and re-enter the console, or type reload!. Either of these will cause the console to reload all of your classes. In short, your classes remain in the state they were in when you first loaded the console, so its something to keep in mind when you're developing and playing around in the console simultaneously.


    Regarding your question from the comments:

    When we call with an instance of the article - @article.article_categories - we get this <ArticleCategory::ActiveRecord_Associations_CollectionProxy:0x007fc46acc5db8>

    That's correct, you get back an association object. Most of the time you don't need to worry about this, as invoking some array methods such as .each and such will give you the concrete objects.

    The collection proxy object, however, lets you perform other active record method calls to further filter the article_categories list if you like. You can do stuff like this, for example:

    article.last.article_categories.where(sport: "curling")
    

    and the article_categories list will be limited to those that match the .where clause filter. You can verify this in the console or rails log by looking at the generated SQL query.

    Hope that helps.