Rails user profile devise :has_many or :has_many_through relationships - ruby-on-rails

I'm wracking my brain about how to start setting up the following set of model relationships in my project. I'm not looking for the complete solution (models, tables, migrations, etc), just a little help to get me going in the right direction. I learn more by struggling on my own :)
In short, I want my Users (created with Devise) to have a set of Inclinations that represent preferences they have to certain things (i.e. warmer weather vs colder weather, watching sports vs playing sports, etc) on a sliding scale (coded 5 to -5, zero excluded). Each Inclination (or InclinationData?) will be in a separate table with a name, description, etc, and all Inclinations will get their data pulled from that table (so I don't repeat myself throughout my application). Also, every User will have one Inclination record for each InclinationData(?) by default.
As for the setup, will I need:
5 tables (3 main ones and two join tables)?
Just three (main three models)?
Four (main three models & a joining table for Inclinations and InclinationData)?
Four (main three models & a joining table for Users & Inclinations)?
Perhaps something simpler like a single Profile or Preferences model that's bound to a User?
I can never figure out how to decide if joining tables are necessary, and this is the most complex association I've ever tried.
I appreciate any help, and please let me know if you need more information to be able to help me out.

You say, all Users have the same set of Inclinations. You can put the description of the Inclinations in one model/table and the users ratings in the joining table UserInclination , according to eabraham:
class User < ActiveRecord::Base
has_many :users_inclinations
has_many :inclinations, through: :users_inclinations
end
class UsersInclination < ActiveRecord::Base
belongs_to :users
belongs_to :inclinations
attr_accessible :user_id, :inclination_id, preference_score
end
class Inclination < ActiveRecord::Base
has_many :users_inclinations
attr_accessible :description, :name, :low_label, :high_label
end
So you can pull the description (i.e. to build the rating form) from Inclinations and store the individual ratings with
an_inclination= Inclination.find_by_name('weather')
user.user_inclination.create(inclination: an_inclination, preference_score: the_users_score)

I think I've reasoned through your request:
Users has_many UserInclinations and Inclinations has_one UserInclinations. Roughly, the models should look like this:
class User < ActiveRecord::Base
has_many :user_inclinations
#default Devise attr_accessible
end
class UserInclinations < ActiveRecord::Base
belongs_to :users
belongs_to :inclinations
attr_accessible :user_id, :inclination_id, preference_score
end
class Inclination < ActiveRecord::Base
has_one :user_inclination
attr_accessible :preference
end

Related

Need to know if I need a third model for joining Users with another model

I have two models, pit and user. Pit is basically a glorified post model. Its a discussion in which users participate. I basically would like it if I could have users "join" a pit and then be associated with that pit. I've done some searching on here and I'm not quite sure whether I need to create another "group" model or not. Users can have many pits and I also want them to able to belong to many pits via join. I'm new at this and really don't want to screw it up so would like some advice before embarking on this task. Thanks for any advice.
Current Pit model
class Pit < ActiveRecord::Base
validates :topic, :author, :summary, presence: true
acts_as_taggable
acts_as_votable
has_many :comments
belongs_to :user
Current User model
class User < ActiveRecord::Base
acts_as_voter
has_many :pits
has_many :comments
Based on my searches via this site and google I'm getting the feeling that I maybe need to create a separate "group" model with a "has_many_through" but I don't want to over complicate things if this is not needed. Thanks.
You need to use a has_and_belongs_to_many association.
This will enable each user to have many pits and each pit can have many users. It will give you access to methods such as:
#user.pits - will return all the pits to which a user belongs
#pit.users - will return all the users who belong to a particular pit
You will need to manually create, via a migration, the join table which should be called pits_users and which will have two columns, pit_id and user_id. You will need to update both your Pit and User models to the following:
class Pit < ActiveRecord::Base
...
has_and_belongs_to_many :users
class User < ActiveRecord::Base
...
has_and_belongs_to_many :pits
When a user joins a pit, you could write code as follows:
#pit.users.create(#current_user)
This will add an entry in the join table consisting of the pit id and user id.

Modeling a Subscription in Rails

So, after thinking on this for a while, I have no idea what the proper way to model this is.
I have a website focused on sharing images. In order to make the users happy, I want them to be able to subscribe to many different collections of images.
So far, there's two types of collections. One is a "creator" relationship, which defines people who worked on a specific image. That looks like this:
class Image < ActiveRecord::Base
has_many :creations
has_and_belongs_to_many :locations
has_many :creators, through: :creations
end
class Creator < ActiveRecord::Base
has_many :images, ->{uniq}, through: :creations
has_many :creations
belongs_to :user
end
class Creation < ActiveRecord::Base
belongs_to :image
belongs_to :creator
end
Users may also tag an image with a subjective tag, which is something not objectively present in the image. Typical subjective tags would include "funny" or "sad," that kind of stuff. That's implemented like this:
class SubjectiveTag < ActiveRecord::Base
# Has a "name" field. The reason why the names of tags are a first-class DB model
# is so we can display how many times a given image has been tagged with a specific tag
end
class SubjectiveCollection < ActiveRecord::Base
# Basically, "User X tagged image Y with tag Z"
belongs_to :subjective_tag
belongs_to :user
has_many :images, through: :subjective_collection_member
end
class SubjectiveCollectionMember < ActiveRecord::Base
belongs_to :subjective_collection
belongs_to :image
end
I want users to be able to subscribe to both Creators and SubjectiveTags, and to display all images in those collections, sequentially, on the home page when they log in.
What is the best way to do this? Should I have a bunch of different subscription types - for example, one called SubjectiveTagSubscription and one called CreatorSubscription? If I do go this route, what is the most efficient way to retrieve all images in each collection?
What you want to use is a Polymorphic Association.
In your case, it would look like this:
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :subscribeable, polymorphic: true
end
The subscriptions table would need to include the following fields:
user_id (integer)
subscribeable_id (integer)
subscribeable_type (string)
This setup will allow a subscription to refer to an instance of any other model, as ActiveRecord will use the subscribeable_type field to record the class name of the thing being subscribed to.
To produce a list of images for the currently logged in user, you could do this:
Subscription.where(user_id: current_user.id).map do |subscription|
subscription.subscribeable.images.all
end.flatten
If the performance implications of the above approach are intolerable (one query per subscription), you could collapse your two types of subscribeables into a single table via STI (which doesn't seem like a good idea here, as the two tables aren't very similar) or you could go back to your initial suggestion of having two different types of subscription models/tables, querying each one separately for subscriptions.

RoR Vocabulary quiz application scalability

I am designing a vocabulary quiz application in ruby/rails.
I have basic model/association setup which will work, but i am worried about scalability.
There will be a set number of words in the application. For simplicity, lets say 100.
A user will be able to proceed to a question, which will be generated from looking at which questions they have had before. The question will provide a word and 4 choices for the definition (one being the definition, three other definitions being randomly chosen).
Here is the way my models and associations are set up currently;
class User < ActiveRecord::Base
has_many :user_questions
end
class UserQuestion < ActiveRecord::Base
belongs_to :user
belongs_to :vocab_word
end
class VocabWord < ActiveRecord::base
has_many :user_question
end
Assuming i were to keep this basic model, which of the following approaches should i use?
Have a set number of UserQuestion objects (100) per user and use
calculated columns to store statistics the users performance on
particular words. (e.g. user 502 has attempted the word 'arid' 3
times and correctlty answered 2 times).
For each question
attempt, create a new UserQuestion object. (e.g. user 502 attempted
to guess 'arid' and was incorrect)
Are either of these approaches scalable? If the application had one million users, the first strategy would have 100 million rows in user_questions. the second could have much more than that.
You're almost there. I would recommend extending your model with the following, plus the slight correction on UserQuestion and User association.
class User < ActiveRecord::Base
has_and_belongs_to_many :user_questions
end
class UserQuestion < ActiveRecord::Base
has_and_belongs_to_many :users
belongs_to :vocab_word
end
class VocabWord < ActiveRecord::base
has_many :user_question
end
class Attempt < ActiveRecord::base
belongs_to :vocab_word
belongs_to :user
belongs_to :user_question
attr_accessible :result
end
You'd need a user_questions_users association table to have a many-to-many association between questions and users. I believe it would be scalable. Make sure you set your indexes correctly.

Rails 2 tables, 1 model

I am relatively new to ruby/rails and I have the following question:
I am working on a scheduling app and have a model named Classes and another named ClassEntries. The relationship between them is that each user can have multiple class entries per semester, each relating to one class. Each record in the Classes table belongs to a specific University. A User can have multiple entries in the ClassEntries table for 1 semester (typically 5). Their schedule is comprised of all their ClassEntries with the same semester ID.
I am not sure whether I should have a third model called Schedule that brings together the info in the ClassEntries and Classes models for the user at hand. I originally wrote this functionality in PHP and I simply used a MySQL JOIN to gather the necessary information. In Rails it seems that there should be a better way to accomplish this.
What would be the best way of going about this in Rails?
Many thanks
So, what you are looking for is pretty much associations in Rails.
You would have the following:
def User < ActiveRecord::Base
has_many :course_entries
has_many :courses, :through => :class_entries
end
def CourseEntry < ActiveRecord::Base
belongs_to :user
belongs_to :course
end
def Course < ActiveRecord::Base
has_many :course_entries
has_many :users, :through => :class_entries
end
With those associations set up, Rails would allow you to do such things like
some_user.courses or some_course.users and it will make the joins through CourseEntry for you.
Let me know if this helps. If you need me to go more in depth let me know.

ActiveRecord relationships between HAVE and IS

So I have the following models in my Ruby on Rails setup: users and courses
The courses need to have content_managers and those content_managers are made up of several individuals in the users model.
I'm a newbie, so bear with me. I was thinking of creating a new model called content_managers that has a user_id and a course_id that links the two tables. It makes sense to me that courses HAVE content_managers. However from the users model, it doesn't make sense that users HAVE content_managers. Some of them ARE content_managers.
From that point of view I believe I'm thinking about it incorrectly and need to set up my ActiveRecord in a different manner from what I'm envisioning. Any help is appreciated.
Thanks!
There's no "have" or "are" in ActiveRecord, only "has_many", "has_one" and "belongs_to". With those tools you should be able to do what you want.
An example:
class Course < ActiveRecord::Base
has_many :content_managers
end
class ContentManager < ActiveRecord::Base
has_many :content_manager_members
has_many :users,
:through => :content_manager_members,
:source => :user
end
class ContentManagerMember < ActiveRecord::Base
belongs_to :course_manager
belongs_to :user
end
class User < ActiveRecord::Base
has_many :content_manager_members
has_many :content_managers,
:through => :content_manager_members
end
Be sure to index these correctly and you should be fine, though navigating from User to Course will be slow. You may need to cache some of this in order to find the level of performance you want, but that's a separate issue that will be uncovered during testing.
Whenever implementing something like this, be sure to load it up with a sufficient amount of test data that will represent about 10x the anticipated usage level to know where the ceiling is. Some structures perform very well only at trivial dataset sizes, but melt down when exposed to real-world conditions.

Resources