Models:
cities.rb:
has_many :cities_users
has_many :users, :through => :cities_users
I have a HABTM (through) between cities and users. I want to view all cities associated with a user. Here's what I have and what the error is:
users.rb
has_many :cities_users
has_many :cities, :through => :cities_users
Controller:
#user = User.find(current_user.id)
#users_cities = #user.cities
I have written a migration that creates the JOIN table:
create_table "cities_users", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "city_id"
end
This is my error (relating to second line of controller code):
uninitialized constant User::CitiesUser
I'm having similar problems creating a city that is associated with a user too.
Many thanks.
You should create new model if you want to use has_many :through association.
Please, consider using has_and_belongs_to_many for direct many-to-many connection with no intervening model.
For any details you can read http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many.
Related
I have a City model which I would like to set up an optional relationship with another city known as a sister city. In order to avoid adding another column to cities I would like to create a join table that holds the association.
create_table :sister_city_mappings do |t|
t.integer :city_id
t.integer :sister_city_id
t.timestamps null: false
end
class City
has_one :sister_city_mapping, :class_name => 'SisterCityMapping',
foreign_key: :city_id
has_one :sister_city, :class_name => 'City', through:
:sister_city_mapping, source: :sister_city
end
class SisterCityMapping
belongs_to :city, :class_name => 'City'
belongs_to :sister_city, :class_name => 'City',
foreign_key: :sister_city_id
end
This kind of works as I can do City.first.sister_city = City.last but I can not handle the relationship through form params as cleanly as I'd like:
City.first.sister_city_id = 2
NoMethodError: undefined method `sister_city_id=' for #<City:0x007f893be1b178>
and trying:
City.first.sister_city = 2
ActiveRecord::AssociationTypeMismatch: City(#70113697769780) expected, got Fixnum(#70113638622260)
I think it's very likely that the associations are incorrect, I have also tried SisterCityMapping has_one :sister_city but haven't been successful.
You really should just add a sister_city_id column to your cities table:
class City
has_one :sister_city, :class_name => 'City'
end
If you really want to use this join table association, you can create the relation like this:
City.first.sister_city = City.find(2)
I have two models Group and Person that I want to have a many-to-many relationship, but I'm unclear on how to manage the relationship itself. I want to be able to create groups and persons separately -- NOT necessarily via a nested model -- and then link persons to groups from the group view/model itself.
Does anyone have any suggestions on how to do so?
I thought of creating a many-to-many relationship via a join model and then accepting nested attributes for the join model in the Group model -- so I believe I will be able to add and remove relationships via the Group view/model. Does this approach make sense?
I would create a PersonGroup model that looks like this:
class PersonGroup < ActiveRecord::Base
has_many :people
has_many :groups
end
And you might also do rails generate migration create_person_group and put this in the up method of the generated migration file:
create_table :person_group do |t|
t.integer :person_id, :null => false
t.integer :group_id, :null => false
t.timestamps
end
add_index :person_group, [:person_id, :group_id], :unique => true
Then in Person:
class Person < ActiveRecord::Base
has_many :person_groups
has_many :groups, :through => :person_groups
end
And in Group:
class Group < ActiveRecord::Base
has_many :person_groups
has_many :people, :through => :person_groups
end
Create a junction table. Junction tables are used when you want to store many-to-many relationships. I don't develop in ROR so I don't know the specifics for ActiveRecord but I am sure that this can help you think about the problem as well.
group_id INTEGER,
person_id INTEGER
I am trying to create a personal inbox message system but couple question are on my mind. First let me explain my system.
Here is my table model
create_table "inboxmessages", :force => true do |t|
t.integer "receiver_id"
t.integer "sender_id"
t.integer "message_id"
t.boolean "isread"
t.boolean "isstarred"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "messages", :force => true do |t|
t.string "subject"
t.text "body"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
The relationship would be has follow
inboxmessages
belongs_to :message
belongs_to :user, :class_name => "User", :foreign_key => "sender_id"
belongs_to :user, :class_name => "User", :foreign_key => "receiver_id"
messages
has_many :inboxmessages
user
has_many :inboxmessages
The problem that i am having his i am uncertain on how to create a message which allows me multiple users. here the schema of the form i am trying to have
Message.subject
Inboxmessage.receiver # but have multiple different user
------------------------------------------------------------------------
Message.body
Inboxmessage.sender = current_user # hidden field
Here are the question that I have regarding building this model/controller/app
1 - Should my new form be in inboxmessages or messages?
2 - Should I use accept_nested_for or should I use nested resources
3 - Is my model/database is okay or not the best?
4 - Are my foreign_key relationship well define?
Thanks in advance!!
I would do something like this:
class User
has_many :mailboxes
has_many :messages, :through => :tags
has_many :tags
end
class Message
has_many :users, :through => :tags
has_many :mailboxes, :through => :tags
has_many :tags
end
class Mailbox
has_many :tags
has_many :messages, :through => :tags
has_many :users, :through => tags
end
class Tag
belongs_to :user
belongs_to :message
belongs_to :mailbox
# this class has "read?", "starred", etc.
end
This enables a message to appear in multiple mailboxes, for multiple users, and each user can have his/her own "read?", "starred", etc. You can limit the logic if you want to ensure that a user has only one copy of a message, i.e. the message is not in two or more mailboxes for the same user.
To improve your schema, read the Rails Guides, especially about associations like these:
belongs_to
has_one
has_many
has_many :through
inverse_of
Also look at the Rails gem acts-as-taggable-on
http://rubygems.org/gems/acts-as-taggable-on
One way to think of a message schema is "a message belongs to a mailbox, and a mailbox has many messages" (this is how Yahoo does it). Another way to think of a message is "a message has many tags, and a mailbox is simply a search by tag" (this is how Gmail does it).
By the way, the Ruby mail gem is excellent. You can use it for creating messages, and look at how it converts headers like from, to, cc, etc.
You asked good questions:
1 - Should my new form be in inboxmessages or messages?
My opinion is that you will get the most benefit if you have a model for the message that is like an email, have a model for a mailbox (or tag). To create a new message, the new form would typically be in ./messages/new.html.erb
2 - Should I use accept_nested_for or should I use nested resources?
Nested resources are fine; see the Rails guide here:
http://guides.rubyonrails.org/routing.html#nested-resources
Nested attributes are also fine; see the API here:
A good rule of thumb is to only nest one level down, because after that it gets more complicated than its worth.
3 - Is my model/database is okay or not the best?
Not the best. A real-world message will typically have a sender and receiver(s). But you're modeling the sender and receiver in the inboxmessages table. Better to model the message with has_many receivers, and use a separate model for the user's interaction with the message for example a table called "marks" with fields "starred", "isread", etc. A mark belongs to a message and belongs to a user. A message has_many marks, and a user has_many marks.
4 - Are my foreign_key relationship well define?
In general, yes. Just be aware that email is surprisingly hard to model. A good rule of thumb is to index your foreign keys. Also look at Rails associations "inverse_of" and use it; this will help with speed and memory use.
I would have my classes set up something like this.
class Inbox
has_many :messages
belongs_to :user
end
class User
has_many :messages
has_one :inbox
end
class Message
belongs_to :user
belongs_to :inbox
has_many recipients, class_name: "User", foreign_key: "recipient_id"
end
Given User and Book models, I've created a join model, ViewedBook, that contains additional attributes. Below is the essence of what I've come up with:
create_table "users"
t.string "username"
end
create_table "books"
t.string "title"
t.integer "user_id"
t.date "authored_date"
end
create_table "books_viewings"
t.integer "book_id"
t.integer "user_id"
t.boolean "finished"
t.date "last_viewed_date"
end
class User
belongs_to :book_viewing
has_many :authored_books,
:class_name => "Book",
:source => :book
has_many :book_viewings
has_many :viewed_books :through => :book_viewings
:order => "book_viewings.last_viewed_date DESC"
has_many :finished_books :through => :book_viewings
:conditions => "book_viewings.finished = TRUE",
:order => "book_viewings.last_viewed_date DESC"
end
class Book
belongs_to :user
has_one :author, :class_name => "User"
end
class BookViewing
belongs_to :book
belongs_to :user
end
I think this works for most of the queries I need to create. However, I want each of the Book objects returned by user.viewed_books to include the finished attribute as well. Further, I will have additional queries like Book.best_sellers that I would also like to scope to a given user so that they also include the finished attribute.
From my limited exposure to ActiveRecord, it appears there's probably an elegant way to manage this and generate efficient queries, but I have yet to find an example that clarifies this scenario.
EDIT: to clarify, the other queries I'm looking for will not themselves be restricted to books that have been finished, but I need to have the finished attribute appended to each book if it exists for the given book and scoped user in book_viewings.
See my answer here https://stackoverflow.com/a/8874831/365865
Pretty much, no there isn't a specific way to do this, but you can pass a select option to your has_many association. In your case I'd do this:
has_many :books, :through => :book_viewings, :select => 'books.*, book_viewings.finished as finished'
I am trying to do this in Rails 3. I create a table (syntax on code examples may not be exactly right, I am trying to recreate from memory):
create_table "persons", :force => true do |t|
t.string "name"
t.integer "guest_of_id"
end
And I want guest_id to reference another row in the persons table. Each person is the guest of only one person. So in the model I set up the association:
class Person < ActiveRecord::Base
belongs_to :GuestOf, :class => "Person", :foreign_key => "guest_of_id"
end
However, when I try to reference the guestOf field
a_person.GuestOf.name
I get the error
undefined method 'eq' for nil:NilClass
Is this possible in Rails? Am I doing something wrong? Am I missing a has_many relationship? I strongly suspect my Google-Fu is failing me. The only possible solution I have found is http://railscasts.com/episodes/163-self-referential-association but he is establishing a many to many relationship and I think it is more complicated than what I am trying to do.
Thanks.
You really should be able to just do:
class Person < ActiveRecord::Base
belongs_to :host, :class => "Person", :foreign_key => "guest_of_id"
has_one :guest, :class => "Person", :foreign_key => "guest_of_id"
end