Forms and Routes for Has Many Through Relation in Rails4 - ruby-on-rails

I am a Rails newbie and trying to create a simple Rails4 application. I have User model which is generated by Devise GEM, Examination model and Participation models are both generated by scaffold generator.
These are my models:
class Examination < ActiveRecord::Base
has_many :participations
has_many :users, :through => :participations
end
class Participation < ActiveRecord::Base
belongs_to :user
belongs_to :examination
end
class User < ActiveRecord::Base
has_many :participations
has_many :examinations, :through => :participations
end
And Database Structure:
create_table "participations", force: true do |t|
t.integer "user_id"
t.integer "examination_id"
t.string "exam_language_preference"
....
end
create_table "users", force: true do |t|
t.string "email"
t.string "first_name"
....
end
create_table "examinations", force: true do |t|
t.string "name"
t.string "shortname"
t.datetime "exam_date"
end
Now, I would like to create the structure to make Users to be able to register to exams. In the index page of Examinations (app/views/examinations/index.html.erb) I want to add a "Register" button just next to default Show, Edit and Destroy buttons for each exam. When user click to "Register" button I want them to see a page where they can choose their exam language preference and submit their registrations.
Also I want a User can only 1 time register for an exam. I mean they can register for many exams but only 1 time for each.
I read the whole Rails Guide but couldn't find the right answer.
How can I do this kind of application?

Well, you're not going to find the answer for your specific question in rails guides. :)
I'd suggest that you read more on:
Validate uniqueness of an attribute using the scope option
Nested resources

Related

How to have a rails model have mulitple user_id's in one table?

I'm developing an assigning system. I need to be able to access the Assignment of the referees to the Game model. The Assignment can have up to 4 referees who are all Users. I believe my associations are correct. My questions are:
Do I need to add an assignment_id to the Game table?
To test my system I will need to seed data eventually. How do I seed the data/setup the Assignment so that each attribute (center_referee, assistant_referee) is a User? I would like this form to be a drop-down inevitably if it makes a difference.
For seed Data, I'm looking for something along the lines of the following (ignore that I'm using name instead of first_name):
Assignment.create(center_referee: user.name, assistant_referee_1: user.name, assistant_referee_2: "user.name", fourth_official: "user.name", game_id: 1)
Do I need to setup up accepts_nested_attributes on the model associations to accomplish all this?
Models:
class User < ApplicationRecord
has_many :assignments
has_many :games, through: :assignments
end
class Game < ApplicationRecord
has_one :assignment
has_many :users, through: :assignments
end
class Assignment < ApplicationRecord
belongs_to :game
belongs_to :user
end
Schema:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "first_name"
t.string "last_name"
t.string "role"
end
create_table "games", force: :cascade do |t|
t.string "home_team"
t.string "away_team"
end
create_table "assignments", force: :cascade do |t|
t.string "center_referee"
t.string "assistant_referee_1"
t.string "assistant_referee_2"
t.string "fourth_official"
t.integer "game_id"
end
I know this is somewhat of a loaded question but I've been scratching my head over this for quite sometime now.
I would like to add to what #Vijay said,
As you want to have multiple user_ids in single table, I would suggest you to name the association, for example: In assignment.rb as belongs_to :center_referee, class_name: 'User'
You can also keep the association optional by using optional: true.
Do I need to add an assignment_id to the Game table?
No, Here Assignment belongs_to Game so id should be present in the Assignment model(as an assignment cannot exist without a game but the game is independent of assignment).
If this was about accessing the assignment for a given game, you can try game.assignment.
Do I need to setup up accepts_nested_attributes on the model associations to accomplish all this?
accepts_nested_attributes is used if you want to accept attributes for the associated model(Assignment) while accepting attributes for the model(Game).
for more info checkout
To test my system I will need to seed data eventually. How do I seed the data/setup the Assignment so that each attribute (center_referee, assistant_referee) is a User? I would like this form to be a drop-down inevitably if it makes a difference.
I don't quiet get your this question, but checkout if this helps:
f.collection_select(:center_referee_id, User.all, :id, :name )
Hope this helped.
In your Assignment class, the belongs_to for game is correctly correlated to the database schema. But, the belongs_to :user is incorrect - there is no assignments.user_id column in the db.
I think you want 4 columns, but each of them referring back to the users table with a different name - is that correct? If so, then your model should have the belongs_to with the appropriate option for class.
Since you mention that these are optional, you will have to also specify the allow_null appropriately.
btw, your games table and the corresponding Game model are also not correlating correctly.
I think you need to start with only 2 tables, logically model them correctly to satisfy the one-to-one, one-to-many relationships. Once this is done, then "translate" that into the rails model DSL. currently, this all seems very hay-wire.
create_table "assignments", force: :cascade do |t|
t.string "center_referee"
t.string "assistant_referee_1"
t.string "assistant_referee_2"
t.string "fourth_official"
t.integer "game_id"
end
Looks like you currently have this. What you need to do is to have a change of the types to as below:
create_table "assignments", force: :cascade do |t|
t.integer "center_referee_id"
t.integer "assistant_referee_1_id"
t.integer "assistant_referee_2_id"
t.integer "fourth_official_id"
t.integer "game_id"
end
This is going to work because, as you are passing the belongs_to :center_referee, class_name: "User" to the model, ActiveRecord is intelligent enough to query the ids on the user table.

rails admin has_many through relationship leading to errors

I am using rails_admin gem for admin interface.
I have a has_many through relationship which doesn't seem to work with rails admin.
class Company < ActiveRecord::Base
has_many :talent_infos, class_name: 'CompanyTalentInfo'
has_many :talents, through: :talent_infos
end
class CompanyTalentInfo < ActiveRecord::Base
belongs_to :company
belongs_to :talent
end
class Talent < ActiveRecord::Base
has_many :talent_infos, class_name: 'CompanyTalentInfo'
has_many :companies, through: :talent_infos
end
I get error every time I try to create a new company and my guess is that its the first time when rails_admin tries to check the relationships and it doesn't accept my current associations.
The error I get is this file gems/rails_admin-0.7.0/app/views/rails_admin/main/_form_filtering_multiselect.html.haml:21
21 controller.list_entries(config, :index, field.associated_collection_scope, false).map { |o| [o.send(field.associated _object_label_method), o.send(field.associated_primary_key)] }.sort_by {|a| [selected_ids.index(a[1]) || selected_ids.si ze, i+=1] }
I get this error
undefined method `klass' for nil:NilClass`
Can anyone help me with this association how can I fix it.
perhaps a late response, but could you compare the relevant part of your database schema with the following and let me know the difference? I believe the relations are setup correctly so that should be the problem.
create_table "companies", force: :cascade do |t|
t.string "name"
end
create_table "company_talent_infos", force: :cascade do |t|
t.string "metadata"
t.integer "company_id"
t.integer "talent_id"
end
create_table "talents", force: :cascade do |t|
t.string "name"
end

How to save class as one of the attributes in ROR model

I am trying to create a model in rails named chat, where I have two columns user1 and user2, and I want to store the user object in these. In grails, I do this simply as
class Chat {
User user1
User user2
Date chatStartedOn
}
and I am done. I did somewhat the same for rails
rails generate model Chat user1:User user2:User chatStartedOn:date
but I run db:migrate it showing me the error
undefined method `User' for #<ActiveRecord::ConnectionAdapters::TableDefinition
my user migrate file
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :encrypted_password
t.string :salt
t.timestamps
end
end
end
Please guide me on how I save user's object in chat table.
Try this
rails generate migration CreateChats
You can then add this code in your Chat migration file
class CreateChats < ActiveRecord::Migration
def change
create_table :chats do |t|
t.integer :user1_id, :references => [:users, :id]
t.integer :user2_id, :references => [:users, :id]
t.date :chatStartedOn
t.timestamps
end
end
end
you would then need to add the associations
Model user.rb
has_one :chat
Model chat.rb
belongs_to :user1, :class_name => "User"
belongs_to :user2, :class_name => "User"

Errors when using Rails has_many :through association

My app consists of exercises that users add to workouts. Users can create exercises or select existing ones.
*** UPDATE ****
I've added a model per Ben's solutions.
I'm receiving errors as below when attempting to add exercises to workouts. Is my syntax wrong? I've attempted soltions like this:
w=Workout.last
e=Exercise.last
w.exercises.build(:exercise => e) # NameError: uninitialized constant Workout::ExercisesWorkout
w.exercises_workouts.create(:exericse_id => 1) #NameError: uninitialized constant Workout::ExercisesWorkout
I'm confused by the new methods attached from the association as well as "_" or not, camelCase, Pluralize, symbol..etc.
Rails seems to be looking for the class ExercisesWorkout yet I define "exercises_workouts" and/or ExercisesWorkouts.
Thanks.
I'm having trouble adding exercises to workouts from the rails console. 2 potential issues that I see:
I don't know the proper syntax to do this (build, create, find)
Application setup properly (join table, models,..etc.)
Please let me know where my error is and if there is a better structure / association to use.
Thank you.
Models:
class Exercise < ActiveRecord::Base
has_many :workouts, :through => :exercises_workouts
has_many :exercises_workouts
end
class Workout < ActiveRecord::Base
has_many :exercises, :through => :exercises_workouts
has_many :exercises_workouts
end
class ExercisesWorkouts < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
end
schema.db:
ActiveRecord::Schema.define(version: 20141129181911) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "exercises", force: true do |t|
t.string "name"
t.string "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "exercises_workouts", id: false, force: true do |t|
t.integer "exercise_id", null: false
t.integer "workout_id", null: false
end
create_table "workouts", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
ERROR:
w=Workout.new #create new workout
w.name = 'test' #name workout
w.save #save workout
e1=Exercise.new #create new exercise
e1.name='press' #name exercise
e1.save #save exercise
#I'm not sure of the syntax here... I've tried alot between create, build using symbols and finds...., this is just one example..
w.exercises.create(e1) #NameError: uninitialized constant Workout::ExercisesWorkout
You also need a model for the join table:
class ExercisesWorkouts < ActiveRecord::Base
belongs_to :exercise
belongs_to :workout
end
Here is answer that covers join tables in more detail, if you're interested:
What would the joining table called in this case in Rails 3.1?

Correct Routing for :has_many :through

I am trying to set up a Many-to-Many association between 2 objects. I have gone through several tutorials and have been able to correctly set up the model. My problem is that I am having trouble setting up the correct routes so I can view the full relationship... something like only displaying the products from a specific category (/categories/1/products/)
This is how I generated the model:
script/generate scaffold category name:string
script/generate scaffold product name:string
script/generate scaffold categorization category_id:integer product_id:integer
Here is the schema:
ActiveRecord::Schema.define(:version => 20100205210519) do
create_table "categories", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "categorizations", :force => true do |t|
t.integer "category_id"
t.integer "product_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "products", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Here is the 3 model objects:
class Category < ActiveRecord::Base
has_many :categorizations
has_many :products, :through => :categorizations
end
class Product < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations
end
class Categorization < ActiveRecord::Base
belongs_to :product
belongs_to :category
end
Pretty simple and everything seems to be working fine because I can add a product to a category through the console:
#category.categorizations << Categorization.new(:product_id => 1)
I'm sure I need to update the routes.rb file but I don't really know the correct way to do it. This is what I put in the routes file:
map.resources :categories, :has_many => :products
When I try to view products on a category "/categories/7/products/" it just lists all of the products! Does this mean my route is set up correctly and I just need to write a custom action on the products controller (instead of it going to index)? What am I doing wrong here... am I close or way off?!?
Thanks
You're probably not using the data from the route to filter your list of products.
In your index method of the product_controller, you need to do something like:
Category.find(params[:category_id]).products
What you want to do is use nested resources. The general format looks like this:
map.resources :users do |users|
users.resources :posts
end
Read more about it here.
As proposed in this question, you could try adding the request parameter :category_id to your find query.
Always look at the output of rake routes.

Resources