alias for chain methods in rails - ruby-on-rails

I have a self-join association for users.
This is my current data
Click to view data table
My user model
User.rb
class User < ApplicationRecord
belongs_to :mother, class_name: "User", optional: true
belongs_to :father, class_name: "User", optional: true
end
I am able to execute the following in rails console.
irb(main):001:0> user = User.find(1)
(0.4ms) SELECT sqlite_version(*)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "rj", mother_id: 3, father_id: 2>
irb(main):002:0> user.father
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<User id: 2, name: "father", mother_id: 4, father_id: 5>
irb(main):003:0> user.father.father
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 5], ["LIMIT", 1]]
=> #<User id: 5, name: "grandfather", mother_id: nil, father_id: nil>
In short, I am able to get the object of rj's father's father (grandfather) and rj's father's mother (grandmother) if I do user.father.father or user.father.mother
Is there a way for me to create an alias such as user.grandfather and user.grandmother to get the same result instead?

class User < ApplicationRecord
belongs_to :mother, class_name: "User", optional: true
belongs_to :father, class_name: "User", optional: true
has_one :grandfather, through: :father, source: :father
has_one :grandmother, through: :father, source: :mother
end

In User.rb model, add the below methods
def paternal_grandfather
father&.father
end
def paternal_grandmother
father&.mother
end
def maternal_grandfather
mother&.father
end
def maternal_grandmother
mother&.mother
end
Or, In combined form, we can use the methods as
def grandfather
father&.father || mother&.father
end
def grandmother
father&.mother || mother&.mother
end
Use it as user.grandfather and user.grandmother

Related

Add Existing Tag to Article in Rails 6

I'm trying to add existing Tags to Articles.
My models:
# app/models/user.rb
class User < ApplicationRecord
has_many :articles, dependent: :destroy
has_many :tags, dependent: :destroy
end
# app/models/article.rb
class Article < ApplicationRecord
belongs_to :user
has_many :tags
end
# app/models/tag.rb
class Tag < ApplicationRecord
belongs_to :user
belongs_to :article
has_many :articles
validates :name, presence: true, uniqueness: { scope: :user_id }
end
By using the following in the console, I'm able to add a tag to an article.
> a = Article.last
> a.tags.create(name: "unread", user_id: "1")
> a.tags
Tag Load (0.3ms) SELECT "tags".* FROM "tags" WHERE "tags"."article_id" = ? [["article_id", 29]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 6, name: "unread", user_id: 1, article_id: 29, created_at: "2020-12-28 16:05:36", updated_at: "2020-12-28 16:05:36", permalink: "unread">]>
If I try to add this same tag to different article using the same .create, I get a rollback error.
> a = Article.first
> a.tags.create(name: "unread", user_id: "1")
(0.0ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Tag Exists? (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? LIMIT ? [["name", "unread"], ["user_id", 1], ["LIMIT", 1]]
(0.0ms) rollback transaction
Thinking the problem is the .create since that tag already exists, I tried this first_or_create, but that also errored.
> a.tags.first_or_create(name: "unread", user_id: "1")
Tag Load (0.1ms) SELECT "tags".* FROM "tags" WHERE "tags"."article_id" = ? ORDER BY "tags"."id" ASC LIMIT ? [["article_id", 52], ["LIMIT", 1]]
(0.0ms) begin transaction
Tag Exists? (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? LIMIT ? [["name", "unread"], ["user_id", 1], ["LIMIT", 1]]
(0.3ms) rollback transaction
How do I add an existing tag to an article?
Edit:
I've added a join table as prescribed by Max. This has allowed me to save tags to articles via the console.
> t = Tag.where(name: "rails", user_id: "1").first_or_create
Tag Load (0.6ms) SELECT "tags".* FROM "tags" WHERE "tags"."name" = ? AND "tags"."user_id" = ? ORDER BY "tags"."id" ASC LIMIT ? [["name", "rails"], ["user_id", 1], ["LIMIT", 1]]
=> #<Tag id: 8, name: "rails", user_id: 1, created_at: "2020-12-28 21:21:25", updated_at: "2020-12-28 21:21:25", permalink: "rails">
> a.tags << t
(0.0ms) begin transaction
Article Tag Exists? (0.1ms) SELECT 1 AS one FROM "article_tags" WHERE "article_tags"."tag_id" = ? AND "article_tags"."article_id" = ? LIMIT ? [["tag_id", 8], ["article_id", 29], ["LIMIT", 1]]
Article Tag Create (0.2ms) INSERT INTO "article_tags" ("article_id", "tag_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["article_id", 29], ["tag_id", 8], ["created_at", "2020-12-28 21:22:13.567187"], ["updated_at", "2020-12-28 21:22:13.567187"]]
(17.9ms) commit transaction
Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "article_tags" ON "tags"."id" = "article_tags"."tag_id" WHERE "article_tags"."article_id" = ? LIMIT ? [["article_id", 29], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Tag id: 6, name: "unread", user_id: 1, created_at: "2020-12-28 16:05:36", updated_at: "2020-12-28 16:05:36", permalink: "unread">, #<Tag id: 8, name: "rails", user_id: 1, created_at: "2020-12-28 21:21:25", updated_at: "2020-12-28 21:21:25", permalink: "rails">]>
I'm still unclear how to save this from the view.
I have a form in app/views/articles/edit.html.erb:
<%= form_with(model: #article, local: true) do |form| %>
<%= form.label :tag_list, "Tags, separated by comma" %>
<%= form.text_field :tag_list %>
<%= form.submit "Update Tags" %>
<% end %>
In the Articles Controller I assume I'd want the following.
def edit
#article.tags << Tag.where(name: "unread", user_id: "1").first_or_create
#article.save!
end
...or perhaps I need to loop those tags in the model? Something like this:
# app/models/article.rb
def tag_list
tags.map(&:name).join(", ")
end
def tag_list=(names)
self.tags = names.split(",").map do |name|
Tag.where(name: name.strip, user_id: current_user.id).first_or_create!
end
end
# app/controllers/articles_controller.rb
def edit
#article.tags << tag_list
#article.save!
end
But this isn't working. How do I update the tags from a view?
Edit:
I've decided not to update tags by form, so I've marked Max's answer as the solution since he pointed me to the Join Tables which got me on the right track.
If you wish to update this for posterity, I'm sure they will appreciate it.
If you want a tag to ever belong to more then a single article you need a join table:
class Article < ApplicationRecord
has_many :article_tags
has_many :tags, through: :article_tags
end
# rails g model article_tag article:references tag:references
# make sure to add a unique index on article_id and tag_id
class ArticleTag < ApplicationRecord
belongs_to :article
belongs_to :tag
validates_uniqueness_of :tag_id, scope: :article_id
end
class Tag < ApplicationRecord
has_many :article_tags
has_many :articles, through: :article_tags
end
This creates a many to many association between the two tables (an article can have multiple tags, and a tag can belong to multiple articles). This can also be done through has_and_belongs_to_many which does not have a join model but has limited flexibility.
If you want to add an existing tag(s) to an article you can use the shovel operator:
a = Article.last
a.tags << Tag.first
a.tags << Tag.second
But usually you use the tag_ids= setter to set the association from an array of ids:
class ArticlesController
def create
#article = Article.new(article_params)
# ...
end
private
def article_params
require(:article)
.permit(:title, :body, tag_ids: [])
end
end
<%= form_with(model: #article) do |f| %>
# ...
<%= f.collection_checkboxes :tag_ids, Tag.all, :id, :name %>
<% end %>
tag_ids: [] permits an array of ids.
This will automatically add/remove rows in the join table depending on what the user checks.

Rails 6 table association user table in association with items table

Hi I'm trying to create an app that has items users are selling. I have a table for Users selling the item and a table for items, but I'm a little confused about how I should setup the next table for the buyer. I have a separate table that is many to many between User and Item tracking user_id and item_id. Should I be creating a similar table tracking buyer_id and item_id? I want to be able to track what item has been bought from which user and vs versa. User and Buyers are from the same User table.
Thanks!
Edit:
class UsersController < ApplicationController
def my_page
#user = current_user
#seller_items = current_user.seller_orders.map { |so| so.order_items.map { |oi| { item: oi.item } } }.flatten
#seller_items.to_a
end
end
A more complete answer, with less models, and named joins. You might want to "merge" the order and order_items tables, and remove the multiple if you're dealing with singular items for sale, e.g. cars, but for anything that is either bought in bulk or might be sold at the same time as something else you might want this layout:
generate your models:
rails g model User name:string
rails g model Item name:string
rails g model Order order_date:time status:string
rails g model OrderItem order:references item:references multiple:integer
modify create_order to add in the additional references:
def change
create_table :orders do |t|
t.time :order_date, index: true, null: false
t.string :status
t.references :buyer, index: true, null: false, foreign_key: {to_table: :users}
t.references :seller, index: true, null: false, foreign_key: {to_table: :users}
t.timestamps
end
migrate the models:
rake db:migrate
== 20201126090851 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0036s
== 20201126090851 CreateUsers: migrated (0.0039s) =============================
== 20201126090858 CreateItems: migrating ======================================
-- create_table(:items)
-> 0.0030s
== 20201126090858 CreateItems: migrated (0.0032s) =============================
== 20201126091129 CreateOrders: migrating =====================================
-- create_table(:orders)
-> 0.0077s
== 20201126091129 CreateOrders: migrated (0.0081s) ============================
== 20201126091209 CreateOrderItems: migrating =================================
-- create_table(:order_items)
-> 0.0065s
== 20201126091209 CreateOrderItems: migrated (0.0067s) ========================
modify the models to add the joins:
app/models/user.rb
::::::::::::::
class User < ApplicationRecord
has_many :buyer_orders, class_name: "Order", foreign_key: :buyer, inverse_of: :buyer
has_many :seller_orders, class_name: "Order", foreign_key: :seller, inverse_of: :seller
end
::::::::::::::
app/models/item.rb
::::::::::::::
class Item < ApplicationRecord
has_many :order_items, inverse_of: :item
end
::::::::::::::
app/models/order.rb
::::::::::::::
class Order < ApplicationRecord
has_many :order_items, inverse_of: :order
belongs_to :seller, class_name: "User", inverse_of: :seller_orders
belongs_to :buyer, class_name: "User", inverse_of: :buyer_orders
end
::::::::::::::
app/models/order_item.rb
::::::::::::::
class OrderItem < ApplicationRecord
belongs_to :order, inverse_of: :order_items
belongs_to :item, inverse_of: :order_items
end
insert data:
User.create(name: "hello")
User.create(name: "again")
Item.create(name: "whatever")
Order.create(buyer: User.first, seller: User.last, order_date: Time.now())
OrderItem.create(item: Item.first, order: Order.first, multiple: 1)
test the output:
Check the status of the order:
2.7.0 :002 > Order.first
(0.5ms) SELECT sqlite_version(*)
Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<Order id: 1, order_date: "2000-01-01 09:26:22", status: nil, buyer_id: 1, seller_id: 2, created_at: "2020-11-26 09:26:22", updated_at: "2020-11-26 09:26:22">
2.7.0 :003 > Order.first.seller
Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=> #<User id: 2, name: "again", created_at: "2020-11-26 09:25:26", updated_at: "2020-11-26 09:25:26">
2.7.0 :004 > Order.first.buyer
Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "hello", created_at: "2020-11-26 09:25:18", updated_at: "2020-11-26 09:25:18">
Check the "buyer orders" of the first user:
2.7.0 :013 > User.first.buyer_orders
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
Order Load (0.3ms) SELECT "orders".* FROM "orders" WHERE "orders"."buyer_id" = ? LIMIT ? [["buyer_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Order id: 1, order_date: "2000-01-01 09:26:22", status: nil, buyer_id: 1, seller_id: 2, created_at: "2020-11-26 09:26:22", updated_at: "2020-11-26 09:26:22">]>
check the "seller orders" of the second user:
2.7.0 :014 > User.last.seller_orders
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ? [["LIMIT", 1]]
Order Load (0.3ms) SELECT "orders".* FROM "orders" WHERE "orders"."seller_id" = ? LIMIT ? [["seller_id", 2], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Order id: 1, order_date: "2000-01-01 09:26:22", status: nil, buyer_id: 1, seller_id: 2, created_at: "2020-11-26 09:26:22", updated_at: "2020-11-26 09:26:22">]>
for peace of minds sake, check that the first user doesn't have any seller orders:
2.7.0 :015 > User.first.seller_orders
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
Order Load (0.2ms) SELECT "orders".* FROM "orders" WHERE "orders"."seller_id" = ? LIMIT ? [["seller_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.7.0 :016 >
Controller
class UsersController < ApplicationController
def my_page
#user = current_user
#seller_orders = current_user.seller_orders
# remember that you might want to filter this in the future
# from_date = params[:from_date].present? ? params[:from_date] : Time.at(0)
# to_date = params[:to_date].present? ? params[:to_date] : Time.now()
# #seller_orders = #seller_orders.where(order_date: from_date..to_date)
end
end
view (I use haml)
%h1
= #user.username
Seller Orders
- #seller_orders.each do |so|
%table.seller_order{id: "seller_order_#{so.id}"}
%tr
%th Order Date:
%td= so.order_date
%tr
%th Buyer:
%td= so.buyer.username
%tr.spacer
%td{colspan: 2}
%tr
%th Item
%th Multiple
- so.order_items.each do |oi|
%tr
%td= oi.item.name
%td= oi.multiple
seller items
#seller_items = #seller_orders.map{|so| so.order_items.map{|oi| {multiple: oi.multiple, item: oi.item} }.flatten
or possibly (written from memory, not tested)
#seller_items = OrderItem.select("sum(order_items.multiple) as multiple, order_items.item_id as item_id").joins(:orders).joins(:buyer).where("users.id = ?", User.first.id).group("item_id")
You need something in the middle indeed - that's typicall called an "Order" in e-commerce with:
Link to User
Link to Item
but also maybe some more attributes:
Date of the sale
Status (cart/order/paid/delivered)
So indeed - you'll want an additional model there
class Seller < ApplicationRecord
belongs_to :user
has_many selling_items
has_many :items, through: :selling_items
end
class Buyer < ApplicationRecord
belongs_to :user
has_many bought_items
has_many :items, through: :bought_items
end
class Items < ApplicationRecord
end
class BoughtItem < ApplicationRecord
belongs_to :buyer
belongs_to :item
end
class SellingItem < ApplicationRecord
belongs_to :seller
belongs_to :item
end
class User < ApplicationRecord
end

How do you return a single value from a scope on a has_many relation?

How do I return a single record from this scope? I tried both ways.
class Subscription < ApplicationRecord
has_many :invoices, dependent: :destroy
class Invoice < ApplicationRecord
belongs_to :subscription
scope :current, -> do
# where(paid: nil).order(:created_at).last
where(paid: nil).order(created_at: :desc).limit(1).first
end
The first way correctly adds order by ... desc limit 1, but then it executes another query without the where condition!
irb(main):004:0> s.invoices.current
Invoice Load (22.0ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."subscription_id" = $1 AND "invoices"."paid" IS NULL ORDER BY "invoices"."created_at" DESC LIMIT $2 [["subscription_id", 16], ["LIMIT", 1]]
Invoice Load (2.0ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."subscription_id" = $1 [["subscription_id", 16]]
=> #<ActiveRecord::AssociationRelation [#<Invoice id: 8, subscription_id: 16, user_id: 21, paid: "2018-03-15", created_at: "2018-03-14 22:42:48">]>
The second way also does another query, obliterating the correct results.
irb(main):007:0> s.invoices.current
Invoice Load (2.0ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."subscription_id" = $1 AND "invoices"."paid" IS NULL
ORDER BY "invoices"."created_at" DESC LIMIT $2 [["subscription_id", 16], ["LIMIT", 1]]
Invoice Load (2.0ms) SELECT "invoices".* FROM "invoices" WHERE "invoices"."subscription_id" = $1 [["subscription_id", 16]]
=> #<ActiveRecord::AssociationRelation [#<Invoice id: 8, subscription_id: 16, user_id: 21, paid: "2018-03-15", created_at: "2018-03-14 22:42:48">]>
Also, how do I get just the record, not an ActiveRecord::AssociationRelation?
Ruby 5.0.6
You might try something like:
class Invoice < ApplicationRecord
belongs_to :subscription
class << self
def for_subscription(subscription)
where(subscription: subscription)
end
def unpaid
where(paid: nil)
end
def newest
order(created_at: :desc).first
end
end
end
Which, if you have an instance of Subscription called #subscription you could use like:
Invoice.unpaid.for_subscription(#subscription).newest
I believe that should fire only one query and should return an invoice instance.
I replaced the scope with
def self.current
where(paid: nil).order(:created_at).last
end
And it worked. However I don't know why, as this method is in Invoice class, while the relation is ActiveRecord::Associations::CollectionProxy class. I wish I knew why it works. And I wish I knew why scope didn't work and performed two queries.

Rails 5: check if role ID belongs to user belonging to current_user (through association)

In my controllers/common/roles_controller.rb I would like to check if particular role (ID) belongs to current_user company users and if not, redirect to errors_path:
def correct_role
role_user = Role.where(:id => params[:id]).select('user_id').first
company_user = current_user.companies.includes(:users)
redirect_to(errors_path) unless company_user.include? role_user.id
end
Definitions:
role_user - finds user ID for particular role ID ("user_id" is column of roles table)
company_user - finds all user ID who belong to companies, which belong to current_user
models/role.rb
belongs_to :user, optional: true, inverse_of: :roles
accepts_nested_attributes_for :user
enum general: { seller: 1, buyer: 2, seller_buyer: 3}, _suffix: true
enum dashboard: { denied: 0, viewer: 1, editer: 2, creater: 3, deleter: 4}, _suffix: true
models/user.rb
#User has roles
has_many :roles
accepts_nested_attributes_for :roles, reject_if: proc { |attributes| attributes[:name].blank? }
# User has many companies
has_many :accounts, dependent: :destroy
has_many :companies, through: :accounts
models/account.rb
class Account < ApplicationRecord
belongs_to :company
belongs_to :user
accepts_nested_attributes_for :company, :user
end
models/company.rb
has_many :accounts, dependent: :destroy
has_many :users, through: :accounts
At the moment with role_user and company_user I can find both ID, however I cannot do the checking part. How do I do that correctly, please? Thank you for any help!
Update
#sajan code give this in console when I open /common/roles/1/edit (current_user ID=1 and should be allowed to edit):
Parameters: {"id"=>"1"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
(0.6ms) SELECT COUNT(*) FROM "companies" INNER JOIN "accounts" ON "companies"."id" = "accounts"."company_id" WHERE "accounts"."user_id" = ? [["user_id", 1]]
(0.3ms) SELECT "roles".id FROM "roles" WHERE "roles"."user_id" = ? [["user_id", 1]]
#Abhishek Kumar code in console gives:
Parameters: {"id"=>"1"}
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Company Load (0.5ms) SELECT "companies".* FROM "companies" INNER JOIN "accounts" ON "companies"."id" = "accounts"."company_id" WHERE "accounts"."user_id" = ? [["user_id", 1]]
(0.4ms) SELECT "users".id FROM "users" INNER JOIN "accounts" ON "users"."id" = "accounts"."user_id" WHERE "accounts"."company_id" = ? [["company_id", 13]]
Role Load (0.2ms) SELECT "roles"."user_id" FROM "roles" WHERE "roles"."id" = ? ORDER BY "roles"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Update v2
So I'm trying to use this code:
def correct_role
company_user_ids = current_user.companies.map(&:user_ids)
role_user = Role.where(:id => params[:id]).select('user_id').first
unless role_user.user_id.in?(company_user_ids)
redirect_to(errors_path)
end
end
however it redirects to errors_path in any case, this is what I have in console:
Parameters: {"id"=>"1"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Company Load (0.5ms) SELECT "companies".* FROM "companies" INNER JOIN "accounts" ON "companies"."id" = "accounts"."company_id" WHERE "accounts"."user_id" = ? [["user_id", 1]]
(0.4ms) SELECT "users".id FROM "users" INNER JOIN "accounts" ON "users"."id" = "accounts"."user_id" WHERE "accounts"."company_id" = ? [["company_id", 13]]
Role Load (0.3ms) SELECT "roles"."user_id" FROM "roles" WHERE "roles"."id" = ? ORDER BY "roles"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
It seems that a better logic would be to define on the model for the current_user an association through companies and users to roles.
Then you can check:
def correct_role
redirect_to(errors_path) unless current_user.company_user_roles.where(id: params[:id]).exists?
end
It would be a single SQL statement that would return zero or one rows, and execute very quickly with the appropriate indexes in place.
Edit:
To company.rb, add:
has_many :user_roles, through: :users, source: :roles
To user.rb (assuming that current_user is an instance of this model) add:
has_many :company_user_roles, through: :companies, source: :user_roles
Maybe you could do like this:
def correct_role
unless current_user.companies.count > 0 && current_user.role_ids.include?(params[:id])
redirect_to(errors_path)
end
end

Rails: How to call one-to-one relationship in rails

I'm new to rails and I want to know how to fetch a one-to-one relationship. I want to fetch users city. In my postgresql database I have:
cities Table:
city:varchar
zipcode: integer
users Table
name:varchar
city_id:int
and in city and user model I have:
class City < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :city
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
I tried the following in my search controller but didnt work, when logged in:
current_user.city
I get the following error
Processing by SearchController#index as HTML
Parameters: {"utf8"=>"✓", "q"=>"", "criteria"=>"1", "commit"=>"Search"}
User Load (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1
PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1
Completed 500 Internal Server Error in 11ms
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column cities.user_id does not exist
LINE 1: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" =...
^
: SELECT "cities".* FROM "cities" WHERE "cities"."user_id" = $1 LIMIT 1):
why am I suppose to add a user_id column to cities table, when I have cities foreign key in users table? I dont want to add user_id into cities table.
You can use has_one :through association with join table. Some example for you below.
user model:
class User < ActiveRecord::Base
has_one :city, through: :user_city
has_one :user_city
end
city model:
class City < ActiveRecord::Base
belongs_to :user
end
user city join model:
class UserCity < ActiveRecord::Base
belongs_to :city
belongs_to :user
end
migration for join tables:
class JoinUserCity < ActiveRecord::Migration
def change
create_table :user_cities do |t|
t.integer :user_id
t.integer :city_id
end
end
end
Test in rails console:
=> u = User.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:14.595728"], ["updated_at", "2014-12-07 15:47:14.595728"]]
(3.3ms) commit transaction
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> nil
=> c = City.create
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "cities" ("created_at", "updated_at") VALUES (?, ?) [["created_at", "2014-12-07 15:47:24.535039"], ["updated_at", "2014-12-07 15:47:24.535039"]]
(3.3ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):004:0> u.city = c
UserCity Load (0.3ms) SELECT "user_cities".* FROM "user_cities" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "user_cities" ("city_id", "user_id") VALUES (?, ?) [["city_id", 1], ["user_id", 4]]
(1.0ms) commit transaction
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
irb(main):005:0> u.save
(0.1ms) begin transaction
(0.1ms) commit transaction
=> true
=> u = User.last
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> #<User id: 4, created_at: "2014-12-07 15:47:14", updated_at: "2014-12-07 15:47:14">
=> u.city
City Load (0.2ms) SELECT "cities".* FROM "cities" INNER JOIN "user_cities" ON "cities"."id" = "user_cities"."city_id" WHERE "user_cities"."user_id" = ? LIMIT 1 [["user_id", 4]]
=> #<City id: 1, created_at: "2014-12-07 15:47:24", updated_at: "2014-12-07 15:47:24">
take a look at the document of has_one and belogns_to,
belongs_to(name, options = {})
Specifies a one-to-one association with another class. This method should only be used if this class
contains the foreign key. If the other class contains the foreign key, then you should use has_one
instead.
as the user table has the foreign key, you should change your model definition like this
class City < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
belongs_to :city
end

Resources