I'm pretty new to the whole rails MVC concept, and I've been tasked with creating a form which does the following:
takes a test number
shows how many sections there are per test (this is a constant, always 4)
allows a region for the user to enter in their responses for each question in a section. The number of questions in each section changes depending on the test number they get.
I have my view which looks like this:
= form_for [#answer_sheet] do |f|
=f.collection_select(:test_prep_number, Exam.find(:all),:test_prep_number, :test_prep_number, {:include_blank => 'Select your test prep number'})
=f.fields_for :answer_sections do |section_form|
=section_form.label :section
.form-inline
=f.label :A
=radio_button_tag 'answer', 'A'
=f.label :B
=radio_button_tag 'answer', 'B'
=f.label :C
=radio_button_tag 'answer', 'C'
=f.label :D
=radio_button_tag 'answer', 'D'
=f.label :E
=radio_button_tag 'answer', 'E'
My controller looks like this:
def index
#answer_sheet = AnswerSheet.build_with_answer_sections
#answer_section = AnswerSection.new
#section_count = AnswerSection.where("exam_id = ?", params[:test_prep_number).count
end
The issue I'm having right now is I can't seem to wrap my head around creating the correct number of radio buttons. So far, I've managed to generate only one question per section.
I'm assuming I'll need a for loop (which will then require a query to find how many questions per exam section).
Edit: Adding models as requested
Answer sheet model
class AnswerSheet < ActiveRecord::Base
attr_accessible :date, :raw_score, :test_prep_number, :answer_sections, answer_sections_attributes
MAX = 101
validates :test_prep_number, :presence => true
validates :raw_score, :presence => true, :numericality => { :greater_than_or_equal_to => 0, :less_than_or_equal_to => MAX}
validates :date, :timeliness => {:on_or_before => lambda { Date.current }, :type => :date}, :presence => true
belongs_to :user
has_many :answer_sections
Answer Section model
class AnswerSection < ActiveRecord::Base
MAX = 30
attr_accessible :section_score, :answers, :answer_attributes
has_many :answers, :dependent => :destroy
belongs_to :answer_sheet
accepts_nested_attributes_for :answers
validates :section_score, :presence => true, :numericality => { :greater_than_or_equal_to => 0,
:less_than_or_equal_to => MAX }
I would recommend tweaking your models to something the breaks the choice from the users answer:
class Test < ActiveRecord::Base
belongs_to :user
has_many :question
attr_accessible :date, :test_prep_number
validates :test_prep_number, :presence => true
validates :date, :timeliness => {:on_or_before => lambda { Date.current }, :type => :date}, :presence => true
end
class Question < ActiveRecord::Base
MAX = 30
attr_accessible :question_text
has_many :choices, :dependent => :destroy
has_one :answer
belongs_to :test
accepts_nested_attributes_for :answers
end
class Choice < ActiveRecord::Base
attr_accessible :question_id, :choice_text, :correct_choice
belongs_to :question
end
class Answer < ActiveRecord::Base
attr_accessible :choice_id
belongs_to :user
belongs_to :question
end
Related
I am beginning Ruby On Rails through a purchase/resale platform project at school. I'm having an issue with my models when I try to translate them from my relational model.
Firstly, I've modelled my database. Here is simplified the entity-relationship model :
I've then translated it in a relational model :
Finally, I've implemented it in Ruby On Rails.
I've implemented a model Client :
class Client < ApplicationRecord
attr_accessor :name
validates :name, :presence => true
has_many :purchasings, :dependent => :destroy
has_many :sellers, :through => :purchasings
has_many :articles, :through => :purchasings
end
I've implemented a model Seller :
class Seller < ApplicationRecord
attr_accessor :name
validates :name, :presence => true
has_many :purchasings, :dependent => :destroy
has_many :sellers, :through => :purchasings
has_many :articles, :through => :purchasings
end
I've implemented a model Article
class Article < ApplicationRecord
attr_accessor :quantity
validates :quantity, :presence => true
has_one :purchasing, :dependent => :destroy
has_one :client, :through => :purchasings
has_one :seller, :through => :purchasings
end
I've implemented a model Purchasing :
class Purchasing < ApplicationRecord
attr_accessor :client_id, :seller_id, :article_id
belongs_to :client, :class_name => "Client"
belongs_to :seller, :class_name => "Seller"
belongs_to :article, :class_name => "Article"
validates :client_id, :presence => true
validates :seller_id, :presence => true
validates :article_id, :presence => true
end
I've modified the Purchasing database migration :
class CreatePurchasing < ActiveRecord::Migration[5.1]
def change
[...]
add_index :purchasings, :client_id
add_index :purchasings, :seller_id
add_index :purchasings, :article_id
add_index :purchasings, [:client_id, :seller_id], :unique => true
end
def down
[...]
end
end
I know this is incorrect because when I execute the following code on the Rails console :
cl1 = Client.create(:name => "John")
cl2 = Client.create(:name => "James")
sel1 = Seller.create(:nom => "Jack")
sel2 = Seller.create(:nom => "Jil")
a1 = Article.create(:quantity => 5)
p1 = Purchasing.new(:client => cl1, :client_id => cl1.id, :seller => sel1, :seller_id => sel1.id, :article => a1, :article_id => a1.id)
p1.save
p2 = Purchasing.new(:client => cl2, :client_id => cl2.id, :seller => sel1, :seller_id => sel1.id, :article => a1, :article_id => a1.id)
p2.save
p2.save returns true whereas an article can't be sold by a same seller and bought by two clients different.
You are adding the index on incorrect columns on purchasings table. As per the requirement, the article_id and seller_id should not get repeated ideally. So you actually need is a uniqueness constraint on seller_id, and article_id column. You can do so by creating an unique index on the composition of two columns seller_id, and article id on the database layer. You should also add the application layer validation on the purchasing model.
class Purchasing < ApplicationRecord
attr_accessor :client_id, :seller_id, :article_id
belongs_to :client, :class_name => "Client"
belongs_to :seller, :class_name => "Seller"
belongs_to :article, :class_name => "Article"
validates :client_id, :presence => true
validates :seller_id, :presence => true
validates :article_id, :presence => true
validates :article_id, uniqueness: {scope: :seller_id}
end
Now you should also write a database migration to add the unique index on these two columns.
class AddUniquenessConstraintInPurshasing < ActiveRecord::Migration
def change
add_index :purchasings, [:article_id, :seller_id], :unique => true
end
end
How do I create a Factory that has multiple associations that rely on the same parent?
The Parent model:
class Parent < ActiveRecord::Base
has_many :codes
has_many :parent_filters
validates :parent, :presence => true, :uniqueness => true
end
The Fitler model:
class Filter < ActiveRecord::base
has_many :parent_filters
validates :filter, :presence => true, :uniqueness => true
end
The ParentFilter join model:
class ParentFilter < ActiveRecord
belongs_to :parent
belongs_to :filter
validates :filter, :presence => true
validates :parent, :filter, :presence => true, :uniqueness => [ :scope => filter ]
end
The AdhocAttribute model:
class AdhocAttribute < ActiveRecord::Base
has_many :adhoc_mappings
has_many :codes, :through => :adhoc_mappings
has_many :parent_filters, :through => adhoc_mappings
validates :adhoc_attribute, :presence => true, :uniqueness => true
end
The code model:
class Code < ActiveRecord::Base
belongs_to :parent
has_many :adhoc_mappings
has_many :adhoc_attributes, :through => :adhoc_mappings
validates :code, :parent, presence: true
validates :code, uniqueness: {case_sensitive: false}
end
And last but not least, an ad-hoc mapping model. This model allows for each code to be assigned one adhoc attribute per ParentFilter, which is the factory that I'm trying to create. This factory should require that the Parent for both the ParentFilter and the Code be the same (I'll add a custom validation to enforce that once I get a functional factory).
class AdHocMapping < ActiveRecord::Base
belongs_to :code
belongs_to :parent_filter
belongs_to :adhoc_attribute
has_one :company, :through => :parent_filter
validates :code, :parent_filter, presence: true
validates :code, :uniqueness => { :scope => :parent_filter }
end
If I were to create an AdhocMapping using straight ActiveRecord, I would build it up something like this:
p = Parent.where(:parent => "Papa").first_or_create
aa = AdhocAttribute(:adhoc_attribute => "Doodle").first_or_create
f = Filter.where(:filter => "Z..W").first_or_create
c = Code.where(:code => "ZYXW", :parent => p).first_or_create
pf = ParentFilter.where(:parent => p, :filter => f).first_or_create
am = AdhocMapping(:adhoc_attribute => aa, :parent_filter => pf, :code => :c).first_or_create
so that the Code and ParentFilter that is assigned to the AdhocMapping have the same Parent. I've tried a number of different methods to try to get this to work in FactoryGirl, but I can't seem to get it to create the object.
Here is the factory I thought was the closest to what I want:
require 'faker'
FactoryGirl.define do
factory :adhoc_mapping do |f|
transient do
p Parent.where(:parent => Faker::Lorem.word).first_or_create
end
association :parent_filter, :parent => p
association :code, :parent => p
association :adhoc_attribute
end
end
It gives an error of Trait not registered: p
For associations I usually use after(:build) in FactoryGirl, then I can exactly tell what should happen. (association xxx often did not work like I wanted it to, shame on me.)
So in your case I think you can solve it with this:
require 'faker'
FactoryGirl.define do
factory :adhoc_mapping do |f|
transient do
default_parent { Parent.where(parent: Faker::Lorem.word).first_or_create }
parent_filter { FactoryGirl.build(:parent_filter, parent: default_parent) }
code { FactoryGild.build(:code, parent: default_parent) }
adhoc_attribute { FactoryGirl.build(:adhoc_attribute) }
end
after(:build) do |adhoc_mapping, evaluator|
adhoc_mapping.parent_filter = evaluator.parent_filter
adhoc_mapping.code = evaluator.code
adhoc_mapping.adhoc_attribute = evaluator.adhoc_attribute
end
end
end
By using after(:build) it works with either FactoryGirl.create or FactoryGirl.build. In addition with the transient default_parent, you can even explicitly set the parent you want to have when you build your adhoc_mapping. And now after edit you can also pass nil to the attributes and it will not get overridden.
I have products and brands
products model:
class Product < ActiveRecord::Base
attr_accessible :brand_id, :title
belongs_to :brand
validates :title, :presence => true
validates :brand, :presence => {:message => 'The brand no exists'}
end
and the brands model
class Brand < ActiveRecord::Base
attr_accessible :name
validates :name, :presence => true
has_many :products, :dependent => :destroy
end
I want to validate if exist a product with a name in this brand.
I mean I could have 2 products with the same name in different brands but not in the same brand.
You could use the uniqueness validation with a scope:
validates :name, :uniqueness => { :scope => :brand_id }
Note that you have to specify :brand_id instead of :brand, because the validation can't be made on the relation.
If you don't know it, I suggest you to read the Active Record Validations and Callbacks guide.
NB: the syntax {:foo => 'bar'} is replaced (since Ruby 1.9.2) with {foo: 'bar'}.
Following is my models:
class Poll < ActiveRecord::Base
attr_accessible :published, :title
validates :published, :presence => true
validates :title, :presence => true,
:length => { :minimum => 10 }
has_many :choice, :dependent => :destroy
end
class Choice < ActiveRecord::Base
belongs_to :poll
attr_accessible :choice_text, :votes
validates :choice_text, :presence => true
end
I then tried to install the rails admin. I was able to create the choices and polls in the admin, but i was unable to associate a choice with a poll and vice versa.
How can i do it?
First of all in has_many class name should be in plural:
has_many :choices
And you should add attr_accessible poll_id or choice_ids for Model from which you want to edit this association. Or just delete all attr_accessible for first try.
class Poll < ActiveRecord::Base
attr_accessible :published, :title, choice_ids
validates :published, :presence => true
validates :title, :presence => true, :length => { :minimum => 10 }
has_many :choices, :dependent => :destroy
end
class Choice < ActiveRecord::Base
belongs_to :poll
attr_accessible :choice_text, :votes, :poll_id
validates :choice_text, :presence => true
end
There is no attr_accessible in Rails 4. Use accepts_nested_attributes_for instead. More info:
https://github.com/sferik/rails_admin/wiki/Belongs-to-association
Model Code (Attempting to require uniqueness for embed_code)
https://gist.github.com/1427851:
class Link < ActiveRecord::Base
validates :embed_code,
:format => {:with => /^<object type|^<embed src|^<object width|^<iframe src|^<object height|^<iframe width|^<embed id|^<embed width|^<object data|^<div|^<object id/i, :message => "Invalid Input"},
:uniqueness => true
attr_accessible :title, :embed_code, :score
after_initialize :calculate_score, :favs_count
attr_accessor :score, :fav_count
validates :title, :length => { :in => 4..45, :message => "Must be between 4 & 45 characters"}
before_save :resize
has_many :favorites
has_many :favorited, :through => :favorites, :source => :user
belongs_to :user
I've tried validates_uniqueness_of :embed_code and disabling (commenting out) non-critical components of the model such as :before_save :resize