I've looked through similar questions but I'm still a little perplexed about something that seems to be a simple case in Rails.
Let's say I have a model class called Employee. One attribute of an employee is their security_clearance, which is a string that can be None, Some, or Full. No other values are valid. In other applications I'd probably represent this an Employees table that has a foreign key to the SecurityClearances table, which has exactly three rows. The SecurityClearances table has columns entitled code (e.g. SEC_CLEARANCE_NONE, SEC_CLEARANCE_SOME, ...) and value ("None", "Some", "Full").
How do I want to do this in Rails? Do I want has_one :security_clearance on Employee and belongs_to :employee on SecurityClearance? That doesn't seem to be quite right.
It seems nonoptimal to type out the string literals of None, Some, and Full everywhere, especially since the values to be displayed could change (for example, perhaps the string for the Some code will be change to be low clearance instead).
Update:
Now that I think about this some more, don't I really just want a belongs_to :security_clearance on Employee? That would do the trick, right? Employees need to know what their security clearance levels are, but security clearance levels have no tie to a particular employee.
Take a look at this plugin: http://github.com/mlightner/enumerations_mixin/tree/master
It allows you to define this like has_enumerated :security_clearance, besides also caching the SecurityClearance model, etc.
Without the plugin, though, you're right about the relationships.
Also check out the Enum Fields plugin from the GiraffeSoft folks:
http://giraffesoft.ca/blog/2009/02/17/floss-week-day-2-the-enum-field-rails-plugin.html
Related
I have a Rails application that includes tables for surveys and (survey) questions.
A survey has_many questions
A question belongs_to a survey
The surveys are inherently teacher surveys. Now we are introducing student surveys, which are meaningfully different from teacher surveys, with different types of information that we need to store about them, such that they seem to each warrant their own table/model, so I'm thinking we want separate tables for teacher_surveys and student_surveys.
However, the questions are really pretty much the same. Includes things like the question text, type of question (text, checkbox, dropdown), etc. So it seems like questions should remain a single table.
How do I best model this data?
Should the questions table have a teacher_survey_id and a student_survey_id where each is optional but one of the two of them is required?
Should I have join tables for questions_teacher_surveys and questions_student_surveys?
Something else?
There is no easy answer to this question. Separating questions into student_question and teach_question tables does mean you have a slight bit of duplication and you can't query them as a homogenized collection if that happens to be important.
The code duplication can be very simply addressed by using inheritance or composition but there is an increased maintainence burdon / complexity cost here.
But it does come with the advantage that you get a guarentee of referential integrity from the non-nullable foreign key column without resorting to stuff like creating a database trigger or the lack of real foreign key constraints if you choose to use a polymorphic association.
An additional potential advantage is that you're querying smaller tables that can remain dense instead of sparse if the requirements diverge.
sounds like you need to make your surveys table polymorphic and add a type column as an enum with :student and :teacher as options, you can use the type column as a flag to control the different business logic. Once the survey table becomes polymorphic you probably wont need to do anything with your questions table. If you decide to go with this solution its also recommend to add a concern class named Surveyable to hold the needed associations and shared logic between your surveyed models (in your case students and teachers).
I'm working on a Rails 5 app for Guild Wars 2, and I'm trying to figure out a way to serialize and store all of the items in the game without duplicating code or table columns. The game has a public API to get the items from, documented
here: https://wiki.guildwars2.com/wiki/API:2/items
As you can see, all of the items share several pieces of data like ID, value, rarity, etc. but then also branch off into specific details based on their type.
I've been searching around for a solution, and I've found a few answers, but none that work for this specific situation.
Single Table Inheritance: There's way too much variance between items. STI would likely end up with a table over 100 columns wide, with most of them null.
Polymorphic Associations: Really doesn't seem to be the proper way to use these. I'm not trying to create a type of model that gets included multiple other places, I just want to extend the data of my "Item" model.
Multiple Table Inheritance: This looks to me like the perfect solution. It would do exactly what I'm wanting. Unfortunately, ActiveRecord does not support this, and all of the "workarounds" I've found seem hacky and weird.
Basically, what I'm wanting is a single "Item" model with the common columns, then a "details" attribute that will fetch the type-specific data from the relevant table.
What's the best way to create this schema?
One possible solution:
Use #serialize on the details (text) column
class Item
serialize :details, Hash
end
One huge downside is that this is very inefficient if you need to query on the details data. This essentially bypasses the native abstractions of the database.
I was in a similar situation recently. I solved by using Sequel instead of ActiveRecords.
You can find it here:
https://github.com/TalentBox/sequel-rails
And an implmentation example:
http://www.matchingnotes.com/class-table-inheritance-in-rails.html
Good luck
Say you have a Book model that can belong to multiple categories. Categories are predefined, but we might let the user add his own even though most of the time default ones are enough.
What is the best way to deal with that according to you ?
a) a many to many relationship to a Category model ? But isn't that overkill given the fact default categories are enough most of the time ?
b) booleans for each default category and an extra string for user entered categories
C) just a string and the use of serialize ? But then it might be a bit more tricky to use checkbox inputs for the default choices
d) any other suggestion of yours...
Thanks for your time!
Thanks
You'll probably want to use HABTM. It's not overkill if you're ever going to want to collect any sort of information about the various categories; date created, for example. You'll also gain the ability to easily fetch all books for a given category without having to worry about scopes inside the book model.
Also, down the road you might want to be able to add other objects to categories. There's not much overhead, and it's quite simple to set up, so worth going for, IMHO.
For help on implementing HABTM with checkboxes, check out the HABTM Checkboxes railscast. For a discussion of the differences between HABTM and has_many :through, I really recommend the Two Many-to-Many railscasts.
Let's assume this model:
Movie
- Title: String
- Has many:
- Alternative Title: String
My questions is, how should I store the alt. title attribute? I am deciding between three approaches:
Separate AR model: probably an overkill
CSV in a signle DB column
Serialized array in single DB column
The latter two seems logically equivilent. I am leaning towards the CSV approach. Can anyone give some advise on this? What would be the implications on speed and searchability?
If a movie can have many titles, it makes most sense to have a Title model and give the Movie model a has_many :titles relation, especially if you later on decide to add more metadata about titles. It may seem like overkill, but I think it will be the least hassle in the long run. Furthermore, I think that a movie's "main" title should be a Title object as well, perhaps with an is_main_title or similar attribute to distinguish it from the others.
If most of the time you only use the primary title, I'll go with your CSV option.
If most of the time you use all the titles, I'll put all the titles (primary and secondary) inside a single CSV column (named "titles") and just get the first when the primary is needed (with a helper function).
Why?
Because it makes things simple- and if the time has come, like Jordan said, that you need another attribute you can always migrate to a separate model.
Until then, YAGNI.
I would also vote for a separate model even though it seems like overkill it will allow you to basically follow the Rails way the easiest. However, if you choose not to reap the benefits of all the baked in magic associated with associations, then I would recommend YAML or JSON over CSV. CSV is quite simple, but Rails has baked in support for YAML serialization and would probably be the easiest solution. Check out RDoc on #serialize. For the given example this would basically amount to:
class Movie < ActiveRecord::Base
serialize :alternate_titles
end
With that, Rails would handle a lot of the drudgery for you and you'll have a nice array of alternate titles always available.
I have an app with an Account model. Each Account belongs_to a sport, which I would usually have as a Sport model and in the DB. But as this isn't really something that will change and is not administered by the end users I thought that it might be better to put it as an integer column in the Account model and map to a hash using a class variable.
However, I need each sport to have many player_positions (which are specific to each sport). So I thought maybe I could do something like:
##player_positions = {:rugby => [position_1, ..., ...]}
Is this good practice for static data like this or should I stick to putting it in the DB as it is relational?
I also thought maybe I could use a yaml file but not sure how I could set that up.
I would stick in the database because you are relating more than one thing to the sport (positions in addition to Account). It makes life easier if you want to use belongs_to and it will allow you to easily use SQL for counts and such (e.g., you can group accounts by sport and get a count of accounts by sport).
I have used an enumerated list of constants in some cases as opposed to tables. For example, I had a simple app with a user type of ADMIN, EDITOR and READER. Rather than put that in a roles table in the DB, I just made those constants on the User class and added a string column to the users table for role.
You don't have to use the database. http://railscasts.com/episodes/189-embedded-association might give you ideas.