Scope a Post with his association table - ruby-on-rails

I would like to know how we can scope a Post with his association table ?
I explain what I want to do it will be more clear. I actually have a scaffold :ranch, and a scaffold :staff. And when I create a new Staff, I can choose to associated it with many ranches. So I have an other table to reference this association :ranch_staff.
So if I want to scope the staff to display only which are associated with the actual #ranch, how can I do that ?

Something like this should do the trick:
migration:
class Init < ActiveRecord::Migration
def change
create_table :ranches do |t|
t.string "name"
end
create_table :staffs do |t|
t.string "name"
end
create_table :ranches_staffs, id: false do |t|
t.belongs_to :ranch
t.belongs_to :staff
end
end
end
app/models/staff.rb:
class Staff < ActiveRecord::Base
has_and_belongs_to_many :ranches
end
app/models/ranch.rb:
class Ranch < ActiveRecord::Base
has_and_belongs_to_many :staffs
end
run in rails console:
staff1 = Staff.create(name: "staff1")
staff2 = Staff.create(name: "staff2")
staff3 = Staff.create(name: "staff3")
staff4 = Staff.create(name: "staff4")
ranch1 = Ranch.create(name: "ranch1")
ranch2 = Ranch.create(name: "ranch1")
staff1.ranches << ranch1
staff2.ranches << ranch1
staff3.ranches << ranch2
staff4.ranches << ranch2
Now you can access ranch1's staff by
ranch1.staffs
=> #<ActiveRecord::Associations::CollectionProxy [#<Staff id: 1, name: "staff1">, #<Staff id: 2, name: "staff2">]>
See association.

Related

ActiveRecord: Scope to get parents with no children created at a specific date

I want to get all parent records with no records created at a specific date on a one-to-many relation in Rails 4 with ActiveRecord and Postgres as database.
Migrations:
class CreateParents < ActiveRecord::Migration
def change
enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
create_table :parents, id: :uuid, default: 'gen_random_uuid()' do |t|
t.string :name
t.timestamps null: false
end
end
class CreateChilds < ActiveRecord::Migration
def change
create_table :childs, id: false do |t|
t.uuid :parent_id, null: false
t.date :created_at, null: false
t.string :name
end
add_foreign_key :childs, :parents
add_index :childs, [:parent_id, :created_at], :unique => true
end
end
Models:
class Parent < ActiveRecord::Base
has_many :childs
end
class Child < ActiveRecord::Base
belongs_to :parent
end
Now I want to get all parents with no child at a specific date with a scope:
class Parent < ActiveRecord::Base
has_many :childs
def self.with_no_childs_created_at(date)
...
end
end
Can anybody help me? I'm going really crazy with this. I tried a lot things with .includes, .references, .where, .not, .joins etc. but I don't get it.
Update 1
One suggested solution looked likes this:
def self.with_no_stats_created_at(date)
joins(:childs).where.not(childs: {created_at: date})
end
But this only works if the parent has already a child created in the past. The SQL should demonstrate the problem:
SELECT "parents".*
FROM "parents"
INNER JOIN "childs" ON "childs"."parent_id" = "parents"."id"
WHERE ("childs"."created_at" != $1) [["created_at", "2016-04-19"]]
Update 2
This solved the problem (suggested by #Ilya):
def self.with_no_childs_created_at(date)
preload(:childs).select {|p| p.childs.all? {|c| c.created_at != date }}
end
You can preload childs to avoid N+1 queries and process it like array:
def self.with_no_childs_created_at(date)
preload(:childs).select {|p| p.childs.all? {|c| c.created_at != date }}
end
You can do it entirely in the database, which is faster most of the time:
scope :without_children_at(date) = {
joins(:childs).where("DATE(created_at) != DATE(?)", date)
}

Can't figure out has_many/belongs_to RAILS 4

My models are:
class CarBrand < ActiveRecord::Base
has_many :car_models
end
class CarModel < ActiveRecord::Base
belongs_to :car_brand
end
and my migrations are
class CreateCarBrands < ActiveRecord::Migration
def up
create_table :car_brands do |t|
t.string "brand", :limit => 20
t.timestamps null: false
end
end
def down
drop_table :car_brands
end
end
class CreateCarModels < ActiveRecord::Migration
def up
create_table :car_models do |t|
t.references :car_brand
t.string "model", :limit => 20
t.timestamps null: false
end
add_index :car_models, :car_brand_id
end
def down
drop_table :car_models
end
end
and i want to get car models according to specific car brand, in database i have both records, but when i type in console it gives error
somecar = CarBrand.where(:brand => 'Toyota')
somecar.car_models
so it doesn't returns me models of toyota, but i have them in database!!!
somecar = CarBrand.where(:brand => 'Toyota') returns an active record relation #<ActiveRecord::Relation [#<..... the main point is that it is a collection. You have to iterate over each item in the collection.
some_cars = CarBrand.where(:brand => 'Toyota')
some_cars.each do |car| puts car.car_model end
or on the first item some_cars.first.car_model
Notice I changed some_car to some_cars, the name of the variable does matter but it is easier to see that it is a collection. Notice I called .car_model (and NOT car_models) on each item, that really is important.
Try like that:-
somecar = CarBrand.where(:brand => 'Toyota')
somecar.first.car_models
As CarBrand.where(:brand => 'Toyota') returns an array.
OR
Try like that:-
somecar = CarBrand.find_by brand: 'Toyota'
somecar.car_models
CarBrand.find_by brand: 'Toyota' will fetch first matching record.

Render children of parent rails 4.0

I have a Contract model that has_many Task_Orders. I am trying to render a view where if I click "Show" for Contract line item, it will display a list of Task_Orders that belong to that Contract.
Here is my Contract schema:
create_table "contracts", force: true do |t|
t.string "contractId"
t.string "contractName"
Here is my Task Order schema:
create_table "task_orders", force: true do |t|
t.integer "contract_Id", limit: 255
t.string "task_orderId"
t.string "task_orderName"
Here is my Contract model:
class Contract < ActiveRecord::Base
has_many :task_orders
end
Here is my Task Order model:
class TaskOrder < ActiveRecord::Base
belongs_to :contract
end
I am not entirely sure how to work with the controller and view to make it happen.... please help. I am using Rails 4.0
Thank you.
foreign_key
Firstly, you need to ensure your foreign_keys are assigned for your associations:
#app/models/task_order.rb
Class TaskOrder < ActiveRecord::Base
belongs_to :contract, primary_key: "contractID", foreign_key: "contract_Id"
end
#app/models/contract.rb
Class Contract < ActiveRecord::Base
has_many :task_orders, primary_key: "contractID", foreign_key: "contract_Id"
end
--
Controller
This should allow you to call the required data from your controller:
#app/controllers/contracts_controller.rb
Class ContractsController < ApplicationController
def show
#contract = Contract.find params[:id]
end
end
#app/views/contracts/show.html.erb
<% for order in #contract.task_orders do %>
<%= order.id %>
<% end %>

N-M relationship unknow attribute error with create method

Here are my migrations:
class CreateTests < ActiveRecord::Migration
def change
create_table :tests do |t|
t.string :value
t.timestamps
end
end
end
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.string :title
t.timestamps
end
end
end
class CreateQuestionsTests < ActiveRecord::Migration
def change
create_table :questions_tests do |t|
t.integer :test_id
t.integer :question_id
t.timestamps
end
end
end
Now in the rails console I created a test object and a question object
test = Test.create(value: "10")
question = Question.create(title: "blablabla")
If now I do test.questions.create(question_id: question.id) I get the following error:
ActiveRecord::UnknownAttributeError: unknown attribute: question_id
How is that?
If you are using has_and_belongs_to_many relationship, you must to have relation table without id and stamps
class CreateQuestionsTests < ActiveRecord::Migration
def change
create_table :questions_tests, :id => false do |t|
t.integer :test_id
t.integer :question_id
end
end
end
I guess that you want to do a Rich association here, if so you should declare relations in your models like this :
Test.rb
class Test < ActiveRecord::Base
has_many :questions_tests
has_many :questions, :through => :questions_tests # here you tell rails that your Test model has many questions if you go through questions_tests
end
Question.rb
class Question < ActiveRecord::Base
has_many :questions_tests
has_many :tests, :through => :questions_tests # here you tell rails that your Question model has many tests if you go through questions_tests
end
QuestionTest.rb
class QuestionTest < ActiveRecord::Base
belongs_to :test
belongs_to :question
end
with this you can traverse the associations table (questions_tests) directly like you want : test.questions.create(question_id: question.id), and you have also this possibility :
test = Test.create(value: "10")
question = Question.create(title: "blablabla")
test.questions_tests << question # or question.questions_tests << test

How to set a models attribute based on a parent model?

I want to set an attribute on a child model when its parent is changed
Here is an example:
create_table "children", :force => true do |t|
t.integer "parent_id"
t.string "parent_type"
t.integer "foo_id"
end
create_table "fathers", :force => true do |t|
t.integer "foo_id"
end
create_table "mothers", :force => true do |t|
t.integer "foo_id"
end
create_table "foos", :force => true do |t|
end
class Foo < ActiveRecord::Base
end
class Child < ActiveRecord::Base
belongs_to :parent, :polymorphic => true
belongs_to :foo
end
class Father < ActiveRecord::Base
belongs_to :foo
end
class Mother < ActiveRecord::Base
belongs_to :foo
end
Now, when I execute the following, I want child.foo_id to be set from parent:
foo = Foo.new {|foo| foo.id = 1}
parent = Father.new {|father| father.foo = foo}
child = Child.new
child.parent = parent
I need foo_id to be set right away, not in a before_validation callback or anything like that.
This is a simplified example, in the real case I have many more polymorphic types. I know this can be accomplished with an after_add callback on a has_many association on Father and Mother, but I'd rather not have to add a has_many association if possible since that requires me to add code in many more places. Is there a way to do this?
I don't clearly understand what you want to achieve.
May be this
parent = Parent.new(foo_id=>123456)
child = Child.new(:parent=>parent,:foo_id=>parent.foo_id)
if parent.save
child.save
end
or
parent = Parent.new(foo_id=>123456)
if parent.save
Child.create(:parent=>parent,:foo_id=>parent.foo_id)
end
not sure if this would work but maybe you could overwrite the setter for parent in the Child model
def parent=(p)
self.foo_id = p.foo_id
super(p)
end

Resources