I am trying to make a little app where users can sign up, login, and be able to view and interact with questions for educational purposes.
I can visualize everything, but I'm having trouble translating that into actual code.
I know that a question model will have
Question Title - as a string or text
Question Answer 1 - as a string or text
Question Answer 2 - as a string or text
Question Answer 3 - as a string or text
Question Answer 4 - as a string or text
Question CORRECT ANSWER 5 - as a string or text
Naturally, I know the strong_params will have to accept these attributes (parameters?) as well.
How can I make a model where the new-question.html.erb form will pass an array of 5 options, with the ability to mark one as correct? On top of this, I would like to shuffle or randomize the answer choices on each page load.
Any help or guidance would be helpful. Michael Hartl's tutorial is great, but I'm not sure if i'm missing things from it or things aren't clicking.
sample for database schema
create_table "questions", force: :cascade do |t|
t.references "quiz_id"
t.string "question_word"
t.string "option1"
t.string "option2"
t.string "option3"
t.string "option4"
t.integer "answer", default: 0
t.integer "score", default: 2
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "quizs", force: :cascade do |t|
t.string "quiz_name"
t.string "notes"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "user_quiz", force: :cascade do |t|
t.references "user_id"
t.references "quiz_id"
t.integer "score", default: 0
end
sample model relationship, there are 4 models, User, Quiz, Question, UserQuiz
class Quiz < ActiveRecord::Base
has_many :questions
has_many :user_quizs
has_many :users, :through => :user_quizs
end
class User < ActiveRecord::Base
has_many :user_quizs
has_many :quizs, :through => :user_quizs
end
class Question < ActiveRecord::Base
belongs_to :quiz
end
class UserQuiz < ActiveRecord::Base
belongs_to :user
belongs_to :quiz
end
for user to choose you can use radio_button_tag, here is link to learn
If the number of answers is always 5 or less, there's nothing wrong using a Question model with 5 text fields for the answers. You can also defaulting the first answer is the correct one and in the view showing the question and answers shuffle the answers.
rails g model Question title:text correct_answer:text answer_1:text answer_2:text ...
You're just getting started so don't bother too much with separate model for Question, Answer and nested form. Keep it simple.
Related
I followed this tutorial for making the exact same comments section on my website and now i want to display the number of comments on the article but I can only have the number of answers to the article without including the number of answers of comments.
I don't want to add a column to my comment model with a reference of the article ID because my website is already online and all the old post will have 0 comments because they will not have this new column.
Any idea of how I can do ? I guess it as something to do with belong_to but on the official doc I cannot find it.
My model/comment.rb
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
has_many :comments, as: :commentable
serialize :report, Array
validates :commenter, presence: true, length: { in: 1..500 }
end
my model/article.rb
class Article < ApplicationRecord
include BCrypt
serialize :view, Array
serialize :upvote, Array
serialize :report, Array
has_many :comments, as: :commentable, dependent: :destroy
validates :title, presence: true, length: { in: 1..60 }
validates :content, presence: true
has_secure_password
end
EDIT:
Maybe I could do a method in my helper with a loop which will count every comment of a comment, but I don't how I could make this loop like Article.find(my_article_id).comments.each do and then I don't know how to do, then maybe I should do like Comment.comments.each do ?
I was thinking to do a recursive method but I always struggle to do recursive method
EDIT2:
schema/article
create_table "articles", force: :cascade do |t|
t.string "title"
t.string "author"
t.string "author_ip"
t.string "password_digest"
t.text "content"
t.string "upvote"
t.integer "upvote_count", default: 0
t.string "view"
t.integer "view_count", default: 0
t.string "report"
t.integer "report_count", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "activate", default: true
t.integer "comments_count", default: 0, null: false
end
schema/comment
create_table "comments", force: :cascade do |t|
t.text "commenter"
t.string "author"
t.string "author_ip"
t.string "date"
t.integer "commentable_id"
t.string "commentable_type"
t.string "report"
t.integer "report_count", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "article_id"
end
EDIT3:
comment migration
class AddCommentsCountToComments < ActiveRecord::Migration[5.2]
def change
add_column :comments, :comments_count, :integer, default: 0, null: false
Comment.reset_column_information # to reset cached values
Comment.find_each do |comment|
comment.update(comments_count: comment.comments.count) # updating old articles comments_counter
end
end
end
You can always use article.comments.size but it's not ideal as it will always make queries to the database.
Another way is to add counter cache comments_count column in your articles model, also you can update comments_count for any article created prior to the counter cache in the same migration.
You can start by adding migration
def change
add_column :articles, :comments_count, :integer, default: 0, null: false
Article.reset_column_information # to reset cached values
Article.find_each do |article|
article.update(comments_count: article.comments.count) # updating old articles comments_count
end
end
Now if you ran the migration and checked in the console for articles made prior to this migration, comments_count should reflect the number of comments for articles.
Now, the last step is to add counter_cache option to your comment model.
The counter_cache option makes sure the number in comments_count column is always updated whenever a comment is added or removed.
belongs_to :commentable, polymorphic: true, counter_cache: true
Now to get the cached value, you can either use:
article.comments.size, as if you use a counter_cache on a has_many association, size will use the cached count directly, and won't make any queries at all.
article.comments_count.
Similarly, to have comment.comments.count or comment.comments_count cached value, you need also to add another counter cache for comments table.
To get all the comments including nested comments for specific article, you need to get the article comments count(outer comments) + each comment comments count.
article.comments_count + article.comments.sum(:comments_count)
To DRY your code if you need to reuse it, you can add a instance method inside your article model as so:
def comments_count_including_nested
comments_count + comments.sum(:comments_count)
end
Then you can call article.comments_count_including_nested
You can get the number of comments and child comments like this:
article.comments.sum { |comment| 1 + comment.comments.count }
However, this will execute one query per parent comment on your article, which is not ideal.
Other ways of doing this:
adding an article_id to all comments, and filling the current ones with a migration
using a counter cache on comments
Perhaps I am approaching this all wrong, I have the following models setup:
The User model has many Questions, a Question belongs_to a User
This means I can easily do: u = User.last.questions and see all of the questions associated with the user last added.
Following this, I have a Payment model that belongs_to a Question. A Question has_one payment meaning I can easily do u = User.last.questions.last.payment, getting the payment details from the question last added by the last added user (daft example perhaps!).
Now the bit that is confusing me
I have a model called PaymentOption which is used to populate a drop down list, for different prices, the PaymentOption belongs_to a Payment and a Payment has_one PaymentOption
This is all working correctly, but the only way for me to find out the details of the payment option, such as the amount, would be to do something like this:
payment_amount = PaymentOption.find(User.last.questions.last.payment.payment_option_id).amount
Ideally I would do something like this:
amount = User.last.questions.last.payment.payment_option.amount
By doing it in the way I have, it is as good as doing two seperate queries, one to get the payment option ID and another to find a detail relating to that particular payment option. Surely there is a better way to approach this?
Thanks in advance
EDIT 1
To clarify my answer, I have included the schema for Payments and PaymentOptions
create_table "payment_options", force: :cascade do |t|
t.integer "amount"
t.text "description"
t.string "title"
t.boolean "user_accessible", default: true
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "payments", force: :cascade do |t|
t.string "email"
t.string "token"
t.integer "question_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "payment_option_id"
end
add_index "payments", ["payment_option_id"], name: "index_payments_on_payment_option_id"
add_index "payments", ["question_id"], name: "index_payments_on_question_id"
I think here is you problem:
I have a model called PaymentOption which is used to populate a drop
down list, for different prices, the PaymentOption belongs_to a
Payment and a Payment has_one PaymentOption
You said you've setup Payment has_one PaymentOption and PaymentOption belongs_to Payment. But in the schema described in the end Payment has a payment_option_id.
In this case, Payment belongs_to PaymenteOption and not the opposite. See this example in the Rails Guide. Account has supplier_id so Supplier has one Account and Account belongs_to Supplier.
Your models should be like this:
class Payment < ActiveRecord::Base
belongs_to :payment_option
end
class PaymentOption < ActiveRecord::Base
has_one :payment # Or even has_many :payments
end
I have a form that creates a new exercise showing which muscle groups are worked. Here's an example of what I want to enter into the DB:
New exercise: name => Pull-up, primary => back, secondary => [biceps, forearms, chest]
How should I set this up? I don't want to store primary and secondary as arrays in the muscle_groups_exercised table because I have future queries that will search for exercises based on a muscle_group that is primary to that exercise.
Here is the schema for this part of the app:
create_table "exercises", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "muscle_groups", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "muscle_groups_exercised", force: true do |t|
t.integer "muscle_group_id"
t.integer "exercise_id"
t.binary "primary"
t.binary "secondary"
end
add_index "muscle_groups_exercised", ["exercise_id"], name: "index_muscle_groups_exercised_on_exercise_id", using: :btree
add_index "muscle_groups_exercised", ["muscle_group_id"], name: "index_muscle_groups_exercised_on_muscle_group_id", using: :btree
Here are the models:
class Exercise < ActiveRecord::Base
has_many :muscle_groups, through: :muscle_groups_exercised
end
class MuscleGroup < ActiveRecord::Base
has_many :exercises, through: :muscle_groups_exercised
end
class MuscleGroupExercised < ActiveRecord::Base
belongs_to :exercise
belongs_to :muscle_group
end
I have an exercises_controller and muscle_groups_controller. I think this code should reside in the exercises_controller and muscle_groups_exercised model but not exactly sure how to do this.
What you are doing looks right to me. I imagine will want to apply muscle groups to an exercise, even if you are going to be searching within muscle groups for exercises later on. I would put the required code within exercises_controller.rb.
Check out Ryan Bates article and video on HAMBTM checkboxes for some help on how to do this. It isn't exactly what you are doing, you will need to apply an additional value for secondary or primary.
BTW, I wouldn't have two columns for secondary and primary, since there is only two values, just have the primary one and assume if it isn't true, then the muscle group is secondary.
Do you have a more specific question? Your schema seems appropriate for your requirements. You may not need both a t.binary primary and a t.binary secondary--you could probably get away with having just one of them to specify whether that entry is primary or secondary.
I have 3 models: Hacks, Votes, Users.
A user can create many hacks.
Each user should be able to vote on each hack ONCE (Rating of 1-5. Rating should be updateable in case of a missclick or whatever).
I thought about the following relations:
Hack.rb
belongs_to :user
User.rb
has_many :hacks
Votes.rb
belongs_to :user
belongs_to :hack
Is that correct or am I missing something?
I thought about getting all the votes like this later on:
Hack.first.votes
What kind of foreign-keys do I have to set up?
In my schema.rb I already successfully set my users<=>hack relation up, without any foreign keys.
ActiveRecord::Schema.define(version: 20141019161631) do
create_table "hacks", force: true do |t|
t.string "url"
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", null: false
t.string "crypted_password", null: false
t.string "salt", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.integer "role"
end
end
Thank you very much in advance!
I think this is what you want to have.
class User.rb
has_many :votes
has_many :hacks, through: :votes
end
class Vote.rb
belongs_to :user
belongs_to :hack
end
class Hack.rb
has_many :votes
end
With this, a hack has many votes through a user.
foreign keys:
votes table: user_id, hack_id
You should be able to do hack.votes
EDIT:
I edited the model to reflect a normal has many through relationship
user -> vote <- hack
a user has many votes
a user has many hacks through votes
a hack has many votes
foreign keys live in the votes table. You can use the following when creating the votes table to indicate the foreign key
t.references user
t.references hack
1-account.rb
class Account < ActiveRecord::Base
belongs_to :transaction
end
class Supplier < Account
end
class Expense < Account
end
2-transaction.rb
class Transaction < ActiveRecord::Base
has_many :accounts
accepts_nested_attributes_for :accounts
end
3-migration schema
create_table "accounts", :force => true do |t|
t.string "name"
t.decimal "debit"
t.decimal "credit"
t.decimal "balance"
t.string "type"
t.integer "transaction_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "transactions", :force => true do |t|
t.string "name"
t.decimal "amount"
t.date "date"
t.string "document"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Question1:
What's the best method to reach supplier and expense in the view (see the picture below)?
Question2:
How can I implement a method that automatically record the transaction amount in expense_debit and supplier_credit, and vice versa? (View screenshot)
I'd suggest an alternative setup. For one thing, your transaction does not record which is the "from" account and which is the "to" account. That's kind of important to know for later.
I recognise that there may be multiple accounts from and to... but debit vs credit should be recorded on a per-transaction basis, not in just one big lump on the Account, otherwise it'll be difficult later to, say, list the change in an account's value between date A and date B (important for tax-returns or quarterly sales reports or whatever).
i don't know about naming, but perhaps a transaction has_many account-transfers and a transfer is "transaction_id, account_id, amount, direction" (where direction is debit/credit).