I have been trying and failing for 2 days now :) to get a list of ideas (posts basically) with likes. Order Desc preferably.
I have scaffolded ideas and users which work fine.
Likes (socialization gem) gives me the headache.
I can add likes and retrieve them. And I can also find out how many likes a specific idea has: idea.likers(User).count
and find out whether a user likes a specific idea: user.likes?(idea)
But I can't do agregates because of the non-standard field names which prohibit me from making a JOIN.
create_table "likes", force: :cascade do |t|
t.string "liker_type"
t.integer "liker_id" (this is/should be user_id)
t.string "likeable_type"
t.integer "likeable_id" (this is/should be idea_id)
t.datetime "created_at"
end
add_index "likes", ["likeable_id", "likeable_type"], name: "fk_likeables"
add_index "likes", ["liker_id", "liker_type"], name: "fk_likes"
Models:
like.rb - empty
user.rb - acts_as_liker
idea.rb - acts_as_likeable
Is there a way to join likes and ideas eg somehow matching liker_id to user_id? Or shall I rename the fields in the table (liker_id to user_id and likeable_id to idea_id)...? And also add these:
like.rb
belongs_to :user
belongs_to :idea
idea.rb
has_many :likes, dependent: :destroy
user.rb
has_many :likes, dependent: :destroy
Thanks in advance!
To specify a different column as foreign key which gets used in joins, you could add foreign_key: ... option to belongs_to as follows:
# app/models/like.rb
belongs_to :user, foreign_key: :liker_id
belongs_to :idea, foreign_key: :likeable_id
See referenced documentation on belongs_to.
You can also specify join conditions yourself as follows:
Idea.joins('inner join likes on ideas.id = likes.likeable_id').where(...)
Related
recently I have a migration that adds a user_id column to the watch_events tables. and thus I want to change the watch_event models to handle belongs_to but with multiple approach
create_table 'users', force: :cascade do |t|
t.integer 'id'
t.integer 'customer_id'
end
create_table 'watch_events', force: :cascade do |t|
t.integer 'customer_id'
t.integer 'user_id'
end
previously
class WatchEvent < ApplicationRecord
belongs_to :user, foreign_key: :customer_id, primary_key: :customer_id
end
what I want:
if watch_event.customer_id is present, i want to use belongs_to :user, foreign_key: :customer_id, primary_key: :customer_id
if watch_event.customer_id is not present, i want to use normal belongs_to :user
how can I achieve this on the watch_event model?
I do not think that Rails supports 'fallback foreign keys' on associations. However, you can write a simple wrapper for your problem. First, relate your WatchEvent class twice to the user model, using your two keys and two 'internal' association names (:user_1 and :user_2). Then, add a 'virtual association reader' (user) and a 'virtual association setter' (user=(new_user)):
class WatchEvent < ApplicationRecord
belongs_to :user_1,
class_name: 'User',
foreign_key: :customer_id
belongs_to :user_2,
class_name: 'User',
foreign_key: :user_id
def user
user_1 || user_2
end
def user=(new_user)
self.user_1 = new_user
end
end
With this solution, the requirements "use customer_id to find user" and "use user_id as fallback foreign key if customer_id is nil or doesn't yield a result" is satisfied. It happens in the association reader method user. When there is a reader method, you'll need a setter method, which is user=(). Feel free to design the setter's internals as required, mine is just a suggestion.
BTW: You may need to re-add the declaration of the foreign primary_key. I omitted that for clarity.
If I understand your question correctly, then what you are looking for is a Polymorphic association.
If you see the code below, what it basically does is create two columns in the watch_events table, watcher_type and watcher_id. And the belongs_to :watcher then uses the watcher_type column to identify which model it should associate to,
create_table 'watch_events', force: :cascade do |t|
t.references 'watcher', polymorphic: true, null: false
end
class WatchEvent < ApplicationRecord
belongs_to :watcher, polymorphic: true
end
ich would like to ask for some help with has_and_belongs_to_many association.
I have the following tables and models:
candidate_job_title_translations -> Candidate::JobTitleTranslation (in a subfolder with table_name_prefix )
create_table "candidate_job_title_translations", force: :cascade do |t|
end
profile_experiences, ProfileExperience
create_table "profile_experiences", id: :serial, force: :cascade do |t|
end
candidate_job_title_translations_profile_experiences, No model
create_table "candidate_job_title_translations_profile_experiences", id: false, force: :cascade do |t|
t.bigint "candidate_job_title_translation_id", null: false
t.bigint "profile_experience_id", null: false
end
The two models are setuped for the association:
class ProfileExperience < ApplicationRecord
has_and_belongs_to_many :candidate_job_title_translations, class_name: 'Candidate::JobTitleTranslation'
end
class Candidate::JobTitleTranslation < ApplicationRecord
has_and_belongs_to_many :profile_experiences, class_name: 'ProfileExperience'
end
My Problem now is, I get a ActiveRecord error, saying job_title_translation_id does not exist, which is correct. It should look for candidate_job_title_translation_id
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column candidate_job_title_translations_profile_experiences.job_title_translati
on_id does not exist
LINE 1: ...ces" ON "candidate_job_title_translations"."id" = "candidate...
I have the feeling I can solve it by not having the table_name_prefix and model structure but, that is not good in terms of my structure.
Maybe you have an idea.
Thanks
Thats not really a good domain model to start with.
If you want a translations table you want to do it something like:
class Position
belongs_to :title
has_many :translated_titles,
through: :title,
source: :translations
end
class Title
has_many :positions
has_many :translations,
class_name: 'Titles::Translation'
end
class Titles::Translation
belongs_to :title
end
You should be more concerned about creating meaningful relations and duplication than "I don't want to have another class, waaah" which is the most common reason for choosing HABTM.
Also when "namespacing" models in Rails the module should be plural:
Good: Titles::Translation
Bad: Title::Translation
This convention is due to the way that ActiveRecord maps tables to tables to classes and the fact that nesting your model inside another model class is not really a good idea.
In Rails 3.2, I have a dictionary with words and references, named "gotowords" which store the word they belong to in word_id and the word they make reference to in reference_id (ie. gotofrom in the models):
create_table "words", :force => true do |t|
t.string "word"
t.text "definition"
end
create_table "gotowords", :force => true do |t|
t.integer "word_id"
t.integer "reference_id"
end
With the models:
class Word < ActiveRecord::Base
has_many :gotowords
has_many :gotofroms, class_name: "Gotoword", foreign_key: "reference_id"
end
class Gotoword < ActiveRecord::Base
belongs_to :word
belongs_to :gotofrom, class_name: "Word", foreign_key: "id"
end
The following query works, but makes another query for each gotofroms.word which is apparently not included:
#words = Word.includes(:gotowords, :gotofroms)
I cannot (for now) refactor like this answer suggests, as the application is pretty huge and it would have too many consequences. That said, I can live with the supplemental query, but it bugs me... Adding inverse_of as is doesn't solve the problem:
has_many :gotowords, inverse_of: :word
has_many :gotofroms, class_name: "Gotoword", foreign_key: "reference_id", inverse_of: :gotofrom
Is there a solution to include Word twice in that configuration?
Try using preload. It works a bit different, but might still help to eliminate duplicated db queries:
#words = Word.preload(:gotowords, :gotofroms)
Problem: Message class with a has_many :through relationship and a has_one relationship both to the User class. Getting a strange query when I try to use the has_one relationship.
I have a Message class with a has_many relationship to a User through message_memberships. Each instance of Message as well as having users linked to it through the has_many through relationship has a creator who is also a User.
Due to laziness I started to log the creators id in a creator_id column on the Message instance (schema below). Every time a message was created I would add the creators id to the column. Every time I wanted to reference the creator I would call User.find(message.creator_id). I was looking in to creating a link between the creator column and a user but I cannot find the right implementation.
The problem I am having is with referencing the :creator_id column in the has_one relationship. I would assume something like this would work
Message.rb
has_one :creator, -> { where id: :creator_id }, class_name: 'User'
But the query that gets called whenever I look for message.creator is this
SELECT "users".* FROM "users" WHERE "users"."message_id" = ? AND "users"."id" = 'creator_id' LIMIT 1 [[nil, 26]]
I am not entirely sure where the WHERE "users"."message_id" = ?. Without it, it looks like the query would be fine. I have no idea how to stop it from happening.
Schema
create_table "messages", force: true do |t|
t.string "title"
t.string "contents"
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.integer "creator_id"
t.string "link"
end
Any help would be greatly appreciated
Try this setup:
class Message
belongs_to :creator, class_name: '::User', foreign_key: :creator_id
end
class User
has_many :authored_messages, class_name: '::Message', inverse_of: :creator
end
I have a relational model between users and courses ("courses" as in "I'm taking a course in statistics"). A class has one teacher (a User) and many students (also Users); a user can teach many classes but also be enrolled in many classes.
I believe the correct way to set up this relationship in my models would be:
class User < ActiveRecord::Base
has_many :taught_courses, class_name: "Course"
has_and_belongs_to_many :enrolled_courses, class_name: "Course"
end
class Course < ActiveRecord::Base
belongs_to :teacher, class_name: "User"
has_and_belongs_to_many :students, class_name: "User"
end
But I have no idea how to set this up in my database (I'm new at this).
Edit:
I have a migration that amounts to this:
def change
create_table :courses_users do |t|
t.belongs_to :course
t.belongs_to :user
end
create_table :courses do |t|
t.string :name
t.integer :teacher_id
t.timestamps
end
end
But when I try to create a new a user in the console, I get an error about user_id:
irb(main):007:0> u.taught_courses.create(name: "Foo bar")
(0.0ms) SAVEPOINT active_record_1
(0.1ms) ROLLBACK TO SAVEPOINT active_record_1
ActiveRecord::UnknownAttributeError: unknown attribute: user_id
Where should this user_id be? Or should I be more specific about my foreign keys?
Edit 2
It turns out I also had to specify foreign_key: "teacher_id" on the has_many relation between Users and Courses for it to work.
Sorry for the misleading question.
The relational model seems reasonable to me. Database setup is discussed in 3.3.2 of http://guides.rubyonrails.org/association_basics.html#updating-the-schema. If there's a particular portion of that section that you find confusing, it would help you asked a more narrow question.
For example, the above section includes the following sample migration. You could explain how this example does not seem to apply to your case.
class CreateAssembliesPartsJoinTable < ActiveRecord::Migration
def change
create_table :assemblies_parts, id: false do |t|
t.integer :assembly_id
t.integer :part_id
end
end
end
The significance of the id: false clause is explained in the last paragraph of the aforementioned section, as follows:
We pass id: false to create_table because that table does not
represent a model. That's required for the association to work
properly. If you observe any strange behavior in a
has_and_belongs_to_many association like mangled models IDs, or
exceptions about conflicting IDs, chances are you forgot that bit.