0

I'm trying to learn Ruby on Rails by building a website. Users can add content: Posts and Pictures. Posts and Pictures can have one or more Tags. I want each relationship to be HABTM so I can easily access posts, pictures via specific tags, and vice versa.

I'm trying to make forms to add tags to posts and pictures. When I submit either form, the error I get is :

POST http://localhost:8080/post/1/tags 500 (Internal Server Error)

when I look at the returned object (this is the same for both forms):

undefined method `post_id' for #Tag id: nil, name: "asdf"> on the line '@tag.save'

I've tried added post_id, picture_id to Tag's attr_accesible; no dice. Thanks for your help! I feel like there's something small I'm simply missing.

My models:

class Tag < ActiveRecord::Base
  attr_accessible :name

  has_and_belongs_to_many :pictures
  has_and_belongs_to_many :posts
  validates :name, :presence => true
  validates :name, :uniqueness =>  { :scope => :post_id }
  validates :name, :uniqueness => { :scope => :picture_id }
end

class Post < ActiveRecord::Base
  attr_accessible :content, :title

  belongs_to :user
  has_and_belongs_to_many :tags
end

class Picture < ActiveRecord::Base
      attr_accessible :image, :name

      belongs_to :user
      has_and_belongs_to_many :tags
end

migrations:

class CreateTags < ActiveRecord::Migration
  def change
    create_table :tags do |t|
      t.string :name
    end

    create_table :pictures_tags, :id => false do |t|
        t.references :picture, :tag
    end
  end
end

class CreatePostsTags < ActiveRecord::Migration
    def change
    create_table :posts_tags, :id => false do |t|
        t.references :post, :tag
    end
  end
end

in my view:

<%= form_for([@post, @post.tags.build]) do |f| %>
    <%= f.label 'tag' %>
    <%= f.text_area :name %>
    <%= f.submit %> 
<% end %>

<% current_user.pictures.each do |p|
<%= form_for([p, p.tags.build], :remote => true) do |f| %>
    <%= f.text_area :name %>
    <%= f.submit 'add tag' %>
<% end %>   
<% end %>

in my TagsController:

def tag_post
    authenticate_user!
    @post = Post.find(params[:id])
    @tag = @post.tags.build(params[:tag])
    @tag.save
    redirect_to edit_post_path(@post)
end

def tag_picture
    authenticate_user!
    @picture = Picture.find(params[:id])
    @tag = @state.picture.build(params[:tag])
    @tag.save
            redirect_to edit_picture_path(@picture)
end

Routes.rb:

post '/posts/:id/tags' => 'tags#tag_post', :as => :tag_post
post '/flow/:id/tags' => 'tags#tag_picture', :as => :tag_picture

Rake routes:

                   posts GET    /posts(.:format)                            posts#index
                         POST   /posts(.:format)                            posts#create
                new_post GET    /posts/new(.:format)                        posts#new
               edit_post GET    /posts/:id/edit(.:format)                   posts#edit
                    post GET    /posts/:id(.:format)                        posts#show
                         PUT    /posts/:id(.:format)                        posts#update
                         DELETE /posts/:id(.:format)                        posts#destroy
        new_user_session GET    /users/sign_in(.:format)                    devise/sessions#new
            user_session POST   /users/sign_in(.:format)                    devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)                   devise/sessions#destroy
           user_password POST   /users/password(.:format)                   devise/passwords#create
       new_user_password GET    /users/password/new(.:format)               devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)              devise/passwords#edit
                         PUT    /users/password(.:format)                   devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)                     devise/registrations#cancel
       user_registration POST   /users(.:format)                            devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)                    devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)                       devise/registrations#edit
                         PUT    /users(.:format)                            devise/registrations#update
                         DELETE /users(.:format)                            devise/registrations#destroy
                    root        /                                           flow#flow
                    root        /                                           flow#home
             posts_index GET    /ramblin(.:format)                          posts#index
               post_tags POST   /posts/:id/tags(.:format)                   tags#tag_post
              picture_tags POST   /flow/:id/tags(.:format)                  tags#tag_picture
            create_picture POST   /flow(.:format)                             pictures#create
                  search POST   /flow/search(.:format)                      flow#flow
codesw1tch
  • 720
  • 10
  • 29

2 Answers2

1

Have a look at the routes that were outputted by running rake routes. There is this line:

post_tags POST    /posts/:id/tags(.:format)    tags#tag_post

which shows you that the path has to be posts (plural) rather than post (singular).

So, you should instead post to

/posts/1/tags

By the way, I know you said you are learning Rails, so it's great you are building a tagging system from scratch - but later you may want to have a look into this awesome gem acts as taggable on. It's super easy and fun. And here is a rails casts that goes over tagging very nicely.

Dennis Hackethal
  • 13,662
  • 12
  • 66
  • 115
  • Thank you for this! Although this wasn't causing the direct issue, it was still a problem with my code that I removed. Thanks!! And thank you for the suggestion of 'acts as taggable on', I saw that gem but like you say thought implementing tagging from scratch would be a useful learning device :) – codesw1tch Feb 13 '13 at 22:00
1

The issue is in

  validates :name, :uniqueness =>  { :sscope => :post_id }
  validates :name, :uniqueness => { :scope => :picture_id }

since those methods expect tag model to have post_id and picture_id attributes that are stored in posts_tags

Bohdan
  • 8,298
  • 6
  • 41
  • 51
  • Thank you very much. With this understanding, it seems like there is no way to do this kind of validation using the built-in Rails validations? I will write a custom validation- – codesw1tch Feb 13 '13 at 21:59