I have two models as follows:
icon.rb
belongs_to :category
attr_accessible :name, :url, :category_id, :icon_for
# == Schema Information
#
# Table name: icons
# id :integer not null, primary key
# name :string(255)
# url :string(255)
# category_id :integer
# icon_for :string(255)
category.rb
has_many :icons
attr_accessible :name, :adult
end
# == Schema Information
#
# Table name: categories
#
# id :integer not null, primary key
# name :string(255)
In the Icons controller
def index
#icons = Icon.where(:icon_for => params[:icon_for])
#category_names_for_icons = ???????
end
I want to get all the category names for the categories the selected icons are for.
category_names = Category.where(:id => #icons.category_id) how to make this a range?
Am I on the right track?
You want to extract the category_id from each of your #icons into an array:
category_names = Category.where(:id => #icons.map(&:category_id))
The Ruby map function iterates over an array, and returns an array. You can think of the &:category_id bit as calling the category_id function on each item in the array.
Related
I am attempting to query a database based on a deep condition. As an example, I would like to find all of the people whose primary pet has a specific breed name:
Person > Pet > Breed > Name
The models might look like this.
class Person
has_many pets
attr_accessor :primary_pet
end
class Pet
belongs_to Person
has_one Breed
end
class Breed
# no belonging relationship
attr_accessor :name
end
Now I could do:
Person.find_each do |person|
puts person if primary_pet.breed.name == 'Norwich Terrier'
end
but I want to figure out the proper query syntax for doing this with a where method. The following does not work:
Person.where(primary_pet.breed.name: 'Norwich Terrier').find_each do |person|
puts person
end
I formalized the model relationships with minor edit, and ended up with this:
class Person < ActiveRecord::Base
has_many :pets
attr_accessor :primary_pet
end
class Pet < ActiveRecord::Base
belongs_to :person
belongs_to :breed
end
class Breed < ActiveRecord::Base
# no belonging relationship
attr_accessor :name
end
The best query that I came up with (that actually worked) was this:
Person.joins(pets: :breed).where(breeds: { name: 'Norwich Terrier' }).where("people.primary_pet = pets.id")
This says to join the relationship for pets and to breeds. This allows using the breeds structure to filter for 'Norwich Terrier'. And to finish it off, to additionally filter by primary_pet_id related to pets.id.
That should get you what you asked for in the question.
Here's how I tested it:
breed_names = ["Beagle", "Collie", "Norwich Terrier", "German Shepard", "Dachshund"]
people_names = ["Barbara", "Connie", "Paula", "Gerald", "Dave"]
# Add breeds
breed_names.each {|name| Breed.create!(name: name) }
# Add people
people_names.each {|name| Person.create!(name: name) }
# Add some pets
people_names.each do |name|
person = Person.find_by(name: name)
breed_names.sample(3).each do |breed|
breed = Breed.find_by(name: breed_name)
Pet.create!(breed_id: breed.id, person_id: person.id)
end
end
# Assign the primary_pet for each person
Person.all.each {|person| pet = person.pets.first ; person.update!(primary_pet: pet) if pet }
# List all people and their pets by breed
Person.all.each do |person|
puts "#{person.name}: #{person.pets.map {|pet| pet.breed.name}}"
end
# List all people and their primary pet breed
Person.all.each do |person|
puts "#{person.name}: #{person.primary_pet ? person.primary_pet.breed.name : ''}"
end
# Run the query
Person.joins(pets: :breed).where(breeds: { name: 'Norwich Terrier' }).where("people.primary_pet = pets.id")
When I create a randomly generate exam I would like to store all the correct answer in an array. The reason that I am doing this is because when I grade the exam I would like to see if the answer is correct by matching the user_answer with the same element in the correct_answer array. Unfortunately, when i use a callback its putting the correct answers in a random order where I cannot match them appropriately.
##controller##
class ExamsController < ApplicationController
def create
exam = current_user.exams.create!(test_bank_questions: TestBankQuestion.all.sample(5))
exam.answers
redirect_to exam_question_path(exam, '1')
end
end
#####Model######
class Exam
include Mongoid::Document
before_create :answers
field :user_answer, type: Array, default: []
field :correct_answers_ids, type: Array, default: []
belongs_to :user
has_and_belongs_to_many :test_bank_questions
#### This is where my problem is ####
#I am trying to get all the id's of the correct answer
#and put them in an array when the object is created
def answers
exam_questions = self.test_bank_questions
exam_questions.each do |question|
answer_choices = question.answer_choices
answer_choices.each do |choice|
if choice.correct_choice == true
self.correct_answers_ids << choice.id.to_s
end
end
end
return correct_answers_ids
end
end
####Model ####
class TestBankQuestion
include Mongoid::Document
field :question_url, type: String
embeds_many :answer_choices
has_and_belongs_to_many :exams
end
###Model ###
class AnswerChoice
include Mongoid::Document
field :choice_url, type: String, default: []
field :correct_choice, type: Boolean, default: []
embedded_in :test_bank_question
end
I'm relatively new to rails and very new to database manipulation.
I'm trying to create a class within the database which contains a number of custom objects within it. These custom objects are also to be stored in the database in a separate table. I've managed to set this up as follows
class MyClass < ActiveRecord::Base
has_many :other_objects, :dependent => destroy
end
class OtherObject < ActiveRecord::Base
belongs_to :my_class
attr_accessible :some_stuff...
end
I've created the appropriate database tables and managed to get it working.
Now what I want to do is have (four) particular instances of "OtherObject"s in my class, which can be accessed by some straightforward identifier, something like
test = MyClass.new
...
test.instance_of_other_object.some_attribute = "blahblah"
Such that this updates the database entry for the associated object. What is the best way to go about this?
That has_many association sets up MyClass#other_objects (and a bunch of other methods) to allow you to easily work with associated records.
You probably want:
my_class.other_objects.each do |other_object|
other_object.update_attributes(:foo => 'bar')
end
If you want a direct SQL update, you can use update_all:
my_class.other_objects.update_all(:foo => 'bar')
Update:
If that's the sort of association you need, you may define a belongs_to association:
class MyClass < ActiveRecord::Base
has_many :other_objects, :dependent => :destroy
# uses :selected_other_object_id
belongs_to :selected_other_object, :class_name => "OtherObject"
end
my_class = MyClass.first
my_class.selected_other_object = other_object # Set the object.
# => #<OtherClass:...>
my_class.selected_other_object_id # ID has been set.
# => 10
my_class.selected_other_object # Retrieve the object.
# => #<OtherClass:...>
my_class.selected_other_object.save # Persist ID and other fields in the DB.
my_class = MyClass.find(my_class.id) # If you fetch the object again...
# => #<MyClass:...>
my_class.selected_other_object_id # The ID is still there.
# => 10
my_class.selected_other_object # You have the ID, you get the object.
# => #<OtherClass:...>
my_class.selected_other_object.foo = "bar" # Access associated object this way.
another_variable = my_class.selected_other_object # Or this way.
Remember however that this does not assume that :selected_other_object is a subset of :other_objects.
Also note that the selected_other_object and selected_other_object= methods are already set up when you set up the association, so you don't have to define these yourself.
This is not a complete answer but I have come up with something that works for getting the object, but not for setting it.
class MyClass < ActiveRecord::Base
has_many :other_objects, :dependent => destroy
def particular_instance
return OtherObject.find_by_id(self.particular_instance_id)
end
end
Where my db schema looks like this
create_table "my_classs", :force => true do |t|
t.integer "particular_instance_id"
end
create_table "other_objects", :force => true do |t|
t.integer "my_class_id"
t.string "some_attribute"
end
Update
In order to set an attribute of the other_object class the update_attributes method can be used
my_class.particular_instance.update_attributes(:some_attribute => "blah")
Use object.dup now for rails 3.1 and on.
a = MyClass.first # finds the first instance of the model "MyClass"
b = a.dup # duplicates the record.
c = a.dup # duplicates the record again.
b.field_name = "blahblah"
c.fielf_name = "blahblah"
b.save # saves the record into the database for object b.
c.save # saves the record into the database for object c.
If you look into your database, you can see that a new record has been created. It is identical to the first record excepted that it has a new id.
Also check Duplicate a model for more information about it.
This is my view:
= form_for(#user) do |f|
= f.autocomplete_field #user.city.name, autocomplete_city_name_users_path
On the second line I am trying to display the association, but I am getting
undefined method `London' for #<User:0x00000129bb3030>
The associations:
User belongs_to :city
City has_one :user
The displayed result in the error message (London) is right, but why I am gettng that error message?
The argument to f.autocomplete_field should be the name of a method. The form builder will send this method to #user to get the correct value. Since the value you're interested in is not in user but in an object owned by user, you have a few options:
Add city_name and city_name= methods to your User class:
# app/models/user.rb
def city_name
city && city.name
end
def city_name=(name)
city.name = name # You'll want to make sure the user has a city first
end
If you don't know how to make sure you have a city, you could create one lazily by changing your city_name= method to this:
def city_name=(name)
build_city unless city
city.name = name
end
Then your form would look like this:
= form_for(#user) do |f|
= f.autocomplete_field :city_name, autocomplete_city_name_users_path
Or you could treat this as a nested object. Add this to User:
accepts_nested_attributes_for :city
And use fields_for in your form:
= form_for(#user) do |f|
= f.fields_for :city do |city_f|
= city_f.autocomplete_field :name, autocomplete_city_name_users_path
I've got a super-class (model) Measurement and two sub-classes: WeightMeasurement and LengthMeasurement.
I've then got a Person class which as many WeightMeasurements and LengthMeasurements.
The issue is when creating a new measurement for a Person, I want to use a shared controller that will handle both weight and length measurements.
However, the way that I would typically build up a Person's measurements would be access them bia the parent (Person). Like person.weight_measurement.build. The problem is that I don't know what to put here... person..build ?
# Base-model, includes "type" column in database.
class Measurement < ActiveRecord::Base
belongs_to :person
end
# model subclass
class WeightMeasurement < Measurement
end
# model subclass
class LengthMeasurement < Measurement
end
class Parent < ActiveRecord::Base
has_many :weight_measurements, :dependent => :destroy
has_many :length_measurements, :dependent => :destroy
end
# Single controller for "Measurements"
class MeasurementsController < ApplicationController
...
def new
person = Person.find(params[:person_id])
#
normally would do this, but because I am using STI,
# I don't know that it is a Person's "weight" measurement we are creating
#
# #measurement = #person.weight_measurements.build
#
...
end
...
end
What I normally do, is to create a hidden field in my form, which contains the type I am trying to create.
<%= hidden_field_tag :type, "weight_measurement" %>
You could also have it as a visible form option (say a radio button or select - instead of the hidden field above)
In your controller, you can do the following then:
if ["length_measurement", "weight_measurement"].include?(params[:type])
#measurement = "Measurement::#{params[:type].classify}".constantize.new(:person => #person)
end