rails self join - ruby-on-rails

I want a user to be able to create a challenge (challenges_created) and other users to be able to offer support to achieve them (challenges_supported). I tried to do this with a self joined challenge model with its resources nested beneath the users resource. I currently have models:
class User < ActiveRecord::Base
attr_accessible :name, :supporter_id, :challenger_id
has_many :challenges_created, :class_name => 'Challenge', :foreign_key => :challenger_id
has_many :challenges_supported, :class_name => 'Challenge', :foreign_key => :supporter_id
end
and
class Challenge < ActiveRecord::Base
attr_accessible :challenger, :completion_date, :description, :duration, :status, :supporter, :title
belongs_to :challenger, :class_name => 'User'
has_many :supporters, :class_name => 'User'
end
I think that I would need full CRUD and corresponding views both for when users are creating challenges and when they are supporting them. Because of this, I created 2 controllers named challenges_created_controller and challenges_supported_controller.
My routes.rb file is:
resources :users do
resources :challenges_created
resources :challenges_supported
end
The problem that I am encountering with this setup is that when I try to create a new challenge at
http://localhost:3000/users/3/challenges_created/new
I receive the message
Showing /home/james/Code/Rails/test_models/app/views/challenges_created/_form.html.erb where line #1 raised:
undefined method `user_challenges_path' for #<# <Class:0x007fb154de09d8>:0x007fb1500c0f90>
Extracted source (around line #1):
1: <%= form_for [#user, #challenge] do |f| %>
2: <% if #challenge.errors.any? %>
The result is the same for the edit action too. I have tried many things but if I were to reference #challenge_created in the form_for then it is not matching the Challenge model.
Can anybody please advise on how what I am doing wrong. Thank you in advance. My schema is:
create_table "users", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "challenger_id"
t.integer "supporter_id"
end
create_table "challenges", :force => true do |t|
t.string "title"
t.text "description"
t.integer "duration"
t.date "completion_date"
t.string "status"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "challenger_id"
t.integer "supporter_id"
end

I think the problem is that you have a challenge_created controller, but you do not have model for it. In you form you specify a user and a challenge so rails tries to find a controller for challenge, not challenge_created. Rails thinks that for a model you have a controller named based on the convention.
I recommend you not to create two different controllers for challenges. Use only one and differenciate on actions. E.g. ou can create a list_created and list_supported action in challenges.

Related

Rails Unidentified Method Issue: undefined method `outlet_id'

In my database I am trying to get a one to many relationship between outlets and articles.
I am getting the following error when that relationship is used:
undefined method `outlet_id' for #<Article:0x007fc353887e58>
Here are the models:
class Article < ActiveRecord::Base
belongs_to :analyst
belongs_to :outlet
has_and_belongs_to_many :loe
attr_accessible :article_body, :author, :distribution, :loe, :most_important, :pubdate, :publication, :state, :submitted, :summary, :title, :url, :analyst_id, :loe_ids, :outlet_id
end
class Outlet < ActiveRecord::Base
has_many :articles, foreign_key: :title
attr_accessible :distribution, :name, :state, :article_ids
end
Here are the schema:
create_table "articles_loes", :id => false, :force => true do |t|
t.integer "article_id"
t.integer "loe_id"
end
create_table "loes", :force => true do |t|
t.string "name"
t.string "customer"
t.integer "article_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "loes", ["article_id"], :name => "index_loes_on_article_id"
create_table "outlets", :force => true do |t|
t.string "name"
t.integer "articles_id"
t.integer "distribution"
t.string "state"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "outlets", ["articles_id"], :name => "index_outlets_on_articles_id"
And here is the chunk of the view that calls on :outlet :
<div class="span4">
<%= f.association :loe %>
<%= f.association :outlet %>
</div>
If anyone has any ideas I'd really appreciate them. I think I might need an index of Outlets in Article? I'm not really sure how to implement that if that is the case. Thanks in advance.
Right now there is no way for your Outlet model to associate with the articles that it has. Once you say belongs_to, you need to have an outlet_id column. So you need to add an outlet_id (integer) column to your Article model and populate it with the id of the outlet they belong to. If an Article can belong to many outlets in that case you need to create a many-to-many relationship through a joint table.

find by reference

so i have 2 models:
create_table "holders", :force => true do |t|
t.string "faceid"
t.integer "badges_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
i need 2 things:
to get all the badges of a certain faceid holder
to get all the holders of a certain badge.
i know its really noobs question but until now i didnt work with references so i dont really understood from the literature how to make the connection.
You actually need a many to many association on your holder and badge models. So you have to options either use has many :through or use has_and_belongs_to_many. The difference between the two can be found here. I am taking the example for has_many :through.
You need to create three models.
class Holder < ActiveRecord:Base
has_many :badges_holders
has_many :badges, :through => :badges_holders
end
class Badge < ActiveRecord:Base
has_many :badges_holders
has_many :holders, :through => :badges_holders
end
class BadgesHolder < ActiveRecord:Base
belongs :badge
belongs :holder
end
And your migration files needs to be:
create_table "holders", :force => true do |t|
t.string "faceid"
t.timestamps
end
add_index "holders", ["badges_id"], :name => "index_holders_on_badges_id"
create_table "badges", :force => true do |t|
t.string "name"
t.text "description"
t.timestamps
end
create_table "badges_holders", :force => true do |t|
t.integer "holder_id"
t.integer "badge_id"
t.timestamps
end
Now you can easily use Holder.find_by_faceid('xyz').badges to find the all hedges held by the holder whose faced is xyz. And Badge.first.holders to get all the holders for the first bedge.
For your question HABTM will be a good option as you do not need any extra field in the join table, so you can just use has_and_belongs_to_many in both of your models and you don't need BadgesHolder model in that case. And for the migration of the join table, replace first line with create_table "badges_holders", :id => false, :force => true do |t| a and remove t.timestamps as the join table for HABTM should not have any other column than the foreign keys.
If it's some Ruby on Rails, you must have 2 models :
class Holder < ActiveRecord:Base
has_many :badges
end
class Badge < ActiveRecord:Base
belongs_to :holder
end
Your entry called badges_id should not be in your holders table ; you should have a holder_id on your "badges" table.
Then, you can simply call
Holder.find_by_faceid('foobar').badges
and
Badge.find(1337).holder
If your badge can belongs to many holders, then you have to write a has_and_belongs_to_many relation.

Proper structure for a join table in Rails 3.2

I am rewriting a PHP based support ticket system in Rails and have run into a snag.
I have my users table created and my tickets table created
create_table "tickets", :force => true do |t|
t.integer "user_id", :null => false
t.integer "department_id", :null => false
t.integer "upload_id", :null => false
t.string "subject", :null => false
t.text "body", :null => false
t.string "status_id", :null => false
t.text "url", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
t.string "fName", :null => false
t.string "lName", :null => false
t.string "seKey", :null => false
t.boolean "isAdmin"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "auth_token"
end
Everything is working great, I am able to create tickets, etc...
Now I am needing to assign 1 or more admins to a ticket and am not sure if I should use a has_and_belongs_to_many relationship or a has_many :through relationship.
The way it is currently setup in PHP is just using a join table that matches a userID with a ticketID. I don't think I will ever need any other data relating to the relationship other than that so is has_and_belongs_to_many the best option?
Also, will this cause an issue since the ticket is already associated with a record in the user table (the original creator)? Basically a ticket will have multiple connections to the users table, one will be the person who submitted it and the rest are users who are assigned to handle it.
Both work; however, personally, I prefer a has_many :through relationship. The reason being is you have access to the join table. To do this generate a model called ticket_user with a field called user_id and ticket_id. Then in the model add
belongs_to :user
belongs_to :ticket
then you can add to the ticket model
has_many :ticket_users
has_many :users :through ticket_user
and this to the user model
has_many :ticket_users
has_many :ticket :through ticket_user
Then to retrieve all of a users tickets do
user.tickets
To get all of a tickets users do
ticket.users
For more info check out this guide
this may work - create two relationships between user and ticket, ticket belongs_to creator, :class_name user and ticket has_many assigned_users, class_name user and in user model has_many created_tickets and has_many assigned_tickets
In your case has_and_belongs_to_many is the extra relationship.
has_and_belongs_to_many :members, :class_name => "User", :join_table => "members_tickets"
You can have to create one table members_users with ticket_id and member_id as fields as shown in http://guides.rubyonrails.org/association_basics.html#choosing-between-has_many-through-and-has_and_belongs_to_many
now you can get list of assigned members like below
#ticket.members # list of members
#ticket.members << #user # add new member

DRYing up my Rails belongs_to and has_many assosiations

I'm working on an app that works out debts and who owes what to whom etc..
Currently it works like this:
create_table "debts", :force => true do |t|
t.string "amount"
t.integer "payer_id"
t.integer "payee_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "people", :force => true do |t|
t.string "name"
t.integer "bank"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
class Debt < ActiveRecord::Base
attr_accessible :amount, :payee_id, :payer_id
belongs_to :payee, :class_name => 'Person'
belongs_to :payer, :class_name => 'Person'
end
class Person < ActiveRecord::Base
attr_accessible :amount, :payee_id, :payer_id
has_many :debts_owed, :class_name => Debt, :foreign_key => "payee_id"
has_many :debts_to_pay, :class_name => Debt, :foreign_key => "payer_id"
end
It's working but I know there must be a simpler way of representing multiple associations to the same model? I've been reading up on has_and_belongs_to_many which looks like the right thing but I'm lost to be honest.
Any help would be appreciated!
Thanks
I don't think a HABTM would help here.
HABTM is for situations where you need a many-to-many relationship, for example, tags. If I have a set of blog posts, I might want to be able to tag them. Blog posts can have many tags, and tags can be used on many blog posts.
That sort of relationship isn't happening in your case. I honestly don't believe there is a better way to set up the associations you have.
For just two lines, that's not too much repetition, BUT if you really wanted to, you could do something like this:
[:payee, :payer].each {|f| belongs_to(f, :class_name => 'Person') }
So that's one line instead of two, but it's probably less clear to read and maintain. It might be worth it if you had a few more lines using the same class_name though.

Using build with has_many :through

I have an Entry model and a Category model, where an Entry can have many Categories (through EntryCategories):
class Entry < ActiveRecord::Base
belongs_to :journal
has_many :entry_categories
has_many :categories, :through => :entry_categories
end
class Category < ActiveRecord::Base
has_many :entry_categories, :dependent => :destroy
has_many :entries, :through => :entry_categories
end
class EntryCategory < ActiveRecord::Base
belongs_to :category
belongs_to :entry
end
When creating a new Entry, I create it by calling #journal.entries.build(entry_params), where entry_params is the parameters from the entry form. If any categories are selected, however, I get this error:
ActiveRecord::HasManyThroughCantDissociateNewRecords in Admin/entriesController#create
Cannot dissociate new records through 'Entry#entry_categories' on '#'. Both records must have an id in order to delete the has_many :through record associating them.
Note that the '#' on the second line is verbatim; it doesn't output an object.
I have tried naming my categories selectbox on the form to categories and category_ids but neither make a difference; if either is in the entry_params, the save will fail. If no categories are selected, or I remove categories from entry_params (#entry_attrs.delete(:category_ids)), the save works properly, but the categories don't save, obviously.
It seems to me that the problem is that an EntryCategory record is attempting to be made before the Entry record is saved? Shouldn't build be taking care of that?
Update:
Here's the relevant parts of schema.rb, as requested:
ActiveRecord::Schema.define(:version => 20090516204736) do
create_table "categories", :force => true do |t|
t.integer "journal_id", :null => false
t.string "name", :limit => 200, :null => false
t.integer "parent_id"
t.integer "lft"
t.integer "rgt"
end
add_index "categories", ["journal_id", "parent_id", "name"], :name => "index_categories_on_journal_id_and_parent_id_and_name", :unique => true
create_table "entries", :force => true do |t|
t.integer "journal_id", :null => false
t.string "title", :null => false
t.string "permaname", :limit => 60, :null => false
t.text "raw_body", :limit => 2147483647
t.datetime "created_at", :null => false
t.datetime "posted_at"
t.datetime "updated_at", :null => false
end
create_table "entry_categories", :force => true do |t|
t.integer "entry_id", :null => false
t.integer "category_id", :null => false
end
add_index "entry_categories", ["entry_id", "category_id"], :name => "index_entry_categories_on_entry_id_and_category_id", :unique => true
end
Also, saving an entry with categories works fine in the update action (by calling #entry.attributes = entry_params), so it does seem to me that the problem is only based on the Entry not existing at the point that the EntryCategory records are attempted to be created.
I tracked down the cause of this error to be within the nested_has_many_through plugin. It seems that the version I had installed was buggy; after updating to the most recent version, my build works again.
Why do you call
self.journal.build(entry_params)
instead of
Entry.new(entry_params)
If you need to create a new entry associated to a specific Journal, given a #journal, you can do
#yournal.entries.build(entry_params)

Resources