Rails model JSON not showing associations - ruby-on-rails

I have the following models:
class Office < ActiveRecord::Base
belongs_to :city
belongs_to :company
end
class Company < ActiveRecord::Base
has_one :acquirer
has_many :offices
has_many :cities, through: :offices
end
class City < ActiveRecord::Base
has_many :offices
end
My Offices controller is set up this way:
class OfficesController < ApplicationController
before_action :set_office, only: [:show, :edit, :update, :destroy]
respond_to :html, :json
def index
respond_with(#offices = Office.all(:include => [:company, :city]))
end
...
And my schema.rb:
create_table "cities", id: false, force: true do |t|
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "companies", id: false, force: true do |t|
t.string "name", null: false
t.string "website"
t.datetime "created_at"
t.datetime "updated_at"
t.string "acquirer_id"
end
create_table "offices", force: true do |t|
t.boolean "headquarters"
t.string "city_id"
t.string "company_id"
t.datetime "created_at"
t.datetime "updated_at"
end
I'm not really sure what's wrong.
All I really want is to show the company_id and city_id columns. I have an Acquisitions Controller that shows these columns in JSON even without the respond_with method. So I don't understand why it works by default in that case and not in this. I'm using Rails 4.0.0 with Ruby 2.0.0.

You should always use integer for defining foreign keys.
Coming back to your question, you can use
respond_with Office.all(:include => [:company, :city])).as_json(:include => [:company,:city])
However, if you have complex JSON responses I would suggest you to look at RABL or jbuilder

I got it to work. Leaving controller as it is, I changed index.json.jbuilder from:
json.array!(#offices) do |office|
json.extract! office, :headquarters
json.url office_url(office, format: :json)
end
to:
json.array!(#offices) do |office|
json.extract! office, :headquarters, :company_id, :city_id
json.url office_url(office, format: :json)
end
Not sure what you guys think of this fix?

Related

has_one association not adding methods to belongs_to class

I have two tables User and UserToken. User has_one: token and UserToken belongs_to :user. I was under the impression this would add UserToken#User method to the UserToken class. However, I am getting:
undefined method 'user' for '#' with the
following 'UserToken.where(user_id: 1).user
Do I not understand the association correctly or have I not set it up right?
UsersController:
def get
user = UserToken.where(user_id: 1).user
render json: user.to_json
end
User Model:
class User < ApplicationRecord
has_one :user_token
end
UserToken Model:
class UserToken < ApplicationRecord
belongs_to :user
end
Migration:
def change
create_table :users do |t|
# This id comes from Auth0
t.datetime :date
t.timestamp :updated_at
t.timestamp :created_at
end
create_table :user_tokens do |t|
t.belongs_to :user
# This comes from plaid when a user signs in
t.string :token
t.timestamp :updated_at
t.timestamp :created_at
end
end
Schema:
ActiveRecord::Schema.define(version: 2019_09_19_004350) do
create_table "user_tokens", force: :cascade do |t|
t.bigint "user_id"
t.string "token"
t.datetime "updated_at"
t.datetime "created_at"
t.index ["user_id"], name: "index_user_tokens_on_user_id"
end
create_table "users", force: :cascade do |t|
t.datetime "date"
t.datetime "updated_at"
t.datetime "created_at"
end
end
What you want is:
UserToken.where(user_id: 1).first.user
or better yet:
UserToken.find_by(user_id: 1).user
You're getting the "undefined method" error because #where returns an ActiveRecord::Relation and an ActiveRecord Relation has no #user method.
UserToken.where(user_id: 1).class.name
#=> "ActiveRecord::Relation"
UserToken.where(user_id: 1).first.class.name
#=> "UserToken"

Create records with associated tables in Rails

I am new to ruby on rails and don't understand how to create and save records using associated tables. I want the controller to take the data create a product record and then create as many property and product properties associated with that product. The property and product property have a one to one relationship. The product can have many properties and product properties.
Properties and product properties are coming in like this:
{"name"=>"color", "value"=>"red"}
{"name"=>"material", "value"=>"cotton"}
My controller works for the creation of the product but I am unsure how to create a loop that will build as may associated product and product properties that come in the array sent from the client.
My controller now:
class SendDataController < ApplicationController
protect_from_forgery with: :null_session
def hi
product = Product.new
product.name = params[:name]
product.upc = params[:upc].to_i
product.available_on = params[:availableon]
product.save
end
end
Below are my models:
class Product < ApplicationRecord
has_many :propertys, dependent: :destroy
has_many :product_propertys, dependent: :destroy
end
class Property < ApplicationRecord
belongs_to :product
has_one :product_property, dependent: :destroy
end
class ProductProperty < ApplicationRecord
belongs_to :property
belongs_to :product
end
Migration:
class CreateProducts < ActiveRecord::Migration[5.2]
def change
create_table :products do |t|
t.string :name
t.string :upc
t.datetime :available_on
t.timestamps
end
end
end
class CreateProductProperties < ActiveRecord::Migration[5.2]
def change
create_table :product_properties do |t|
t.string :value
t.belongs_to :property
t.belongs_to :product
t.timestamps
end
end
end
class CreateProperties < ActiveRecord::Migration[5.2]
def change
create_table :properties do |t|
t.string :name
t.belongs_to :product
t.timestamps
end
end
end
schema:
ActiveRecord::Schema.define(version: 2018_09_22_140824) do
create_table "product_properties", force: :cascade do |t|
t.string "value"
t.integer "property_id"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_product_properties_on_product_id"
t.index ["property_id"], name: "index_product_properties_on_property_id"
end
create_table "products", force: :cascade do |t|
t.string "name"
t.string "upc"
t.datetime "available_on"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "properties", force: :cascade do |t|
t.string "name"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_properties_on_product_id"
end
end
Thanks for any help you can give a new guy!
Your Product Model plurality required, has_many properties & equally has_many product_properties.
Your Property schema will need product_id as an integer. i would avoid using has_one it can get messy, just use has_many or you may require a has_many through
Your ProductProperty Model You'll also need product_id integer & property_id integer adding them as separate migration.
rails db:create add_product_id_to product_properties, product_id:integer
check the migration file product_id that the attribute is in the file
rails db:migrate
Restart server & test in the console.
Once the Models speak, instantiate a Product object, bring it across into Properties & ProductProperties through the respective controllers by setting & in turn making the SendDataController obsolete unless your logic requires this.

undefined method `addresses' for #<User:0x007fc955029380>

I am new to Ruby and Rails, trying to fix an error I constantly get. Not sure how to fix it. Please help..
Route.rb
namespace :my do
namespace :account do
resource :details, :only => [:show, :update]
resources :addresses
end
end
AddressesController
class My::Account::AddressesController < MyController
def index
#addresses = current_user.addresses
end
def new
#address = current_user.addresses.new
end
....
end
Error - undefined method `addresses'
NoMethodError in My::Account::AddressesController#index
undefined method `addresses' for #<User:0x007fc955029380>
Schema.rb for customer addresses and users
create_table "customer_addresses", force: true do |t|
t.integer "customer_id"
t.string "name"
t.string "line_1"
t.string "line_2"
t.string "line_3"
t.string "line_4"
t.string "line_5"
t.string "postcode"
t.string "phone"
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "customer_addresses", ["customer_id"], name: "index_customer_addresses_on_customer_id"
create_table "users", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_reset_token"
t.datetime "password_reset_token_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
User.rb
class User < ActiveRecord::Base
validates :email, :presence => true, :uniqueness => true
has_secure_password
...
end
Customer.rb
class Customer < User
has_many :addresses
def self.register(attributes)
customer = create!(attributes)
return customer
end
def full_name
"#{first_name} #{last_name}"
end
end
Address.rb
class Customer::Address < ActiveRecord::Base
belongs_to :customer
self.table_name = 'customer_addresses'
default_scope { where(:deleted_at => nil) }
validates :line_1, :postcode, :phone, :presence => true
end
You need to define the relationship on both models.
class User < ActiveRedord::Base
has_many :addresses, class_name: 'CustomerAddress', foreign_key: 'customer_id'
end
class Address < ActiveRecord::Base
belongs_to :user, foreign_key: 'customer_id'
end
Add a #current_customer method to your ApplicationController that return a Customer-instance instead of a User-instance:
class ApplicationController
#…
private
def current_customer
current_user && Customer.find_by_id(current_user.id)
end
end
then change your code like this:
class My::Account::AddressesController < MyController
def index
#addresses = current_customer.addresses
end
def new
#address = current_customer.addresses.new
end
#…
end

setting up gravitars for my posts through users

I just set it up so that when a user signs up for my blog it gives them a gravatar in the users index. That works fine but I was thinking of making it so that when that user makes a post it will display their gravatar from the user. I just made a user_id colum to posts through a migration.
here is a copy of my schema
ActiveRecord::Schema.define(version: 20131114141804) do
create_table "comments", force: true do |t|
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.string "post_id"
end
create_table "posts", force: true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.string "user_id"
end
create_table "users", force: true do |t|
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "auth_token"
t.string "password_reset_token"
t.datetime "password_reset_sent_at"
t.string "avatar_url"
end
end
models:
user:
class User < ActiveRecord::Base
has_secure_password
validates_uniqueness_of :email
has_many :posts
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
Post:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
has_many :posts
end
application_helper.rb
module ApplicationHelper
def avatar_url(user)
gravatar_id = Digest::MD5::hexdigest(user.email).downcase
"http://gravatar.com/avatar/#{gravatar_id}.png?s=200"
end
end
was trying to do something new, could anyone help me out and or point me in the right direction?
I'd recommend taking a good look at using paperclip for the attachment process rather than trying to re-invent the wheel. Paperclip's documentation actually uses a user avatar as an example, so it'd be perfect for your use case.
You may want to use a Rails plugin to integrate with Gravatar rather than doing it yourself:
gravtastic
Gravatar Rails plugin

before_create throwing NoMethodError?

schema:
create_table "posts", force: true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
t.integer "total_stars"
t.integer "average_stars"
end
create_table "stars", force: true do |t|
t.integer "starable_id"
t.string "starable_type"
t.integer "user_id"
t.integer "number"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "stars", ["starable_id", "starable_type"], name: "index_stars_on_starable_id_and_starable_type"
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
models:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
end
class Star < ActiveRecord::Base
before_create :add_to_total_stars
belongs_to :starable, :polymorphic => true
protected
def add_to_total_stars
if [Post].include?(starable.class)
self.starable.update_column(:total_stars, starable.total_stars + self.number)
end
end
end
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
has_many :votes, dependent: :destroy
end
So I tried creating a star in the Rails console like this:
post = Post.first
user = User.first
star = post.stars.build(number: 1)
star.user_id = user.id
And everything goes OK 'till here. But when I try to save it:
star.save
I get this error:
NoMethodError: undefined method +' for nil:NilClass from
/home/alex/rails/rating/app/models/star.rb:10:inadd_to_total_stars'
from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:377:in _run__956917800__create__callbacks' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:303:in create_record' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/timestamp.rb:57:increate_record' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/persistence.rb:466:in create_or_update' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-4.0.0/lib/active_record/callbacks.rb:299:inblock in create_or_update' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:383:in _run__956917800__save__callbacks' from
/home/alex/.rvm/gems/ruby-1.9.3-p0/gems/activesupport-4.0.0/lib/active_support/callbacks.rb:80:in
run_callbacks'
What could be the cause?
(I'm using Rails 4)
It looks like the value of Post.first.total_stars is nil. Going by your schema and model examples you allow null in the database and do not validate it's presence in ActiveRecord.
If it makes sense to default this value to 0, then you should set the default value in the schema.
So I would add the following to your schema:
create_table "posts", force: true do |t|
# ...
t.integer "total_stars", null: false, default: 0
end
And the following validation in your model:
class Post < ActiveRecord::Base
has_many :stars, :as => :starable, :dependent => :destroy
belongs_to :user
validates :total_stars, presence: true
end
As an aside, I would get rid of total_stars altogether and let rails do this for you with the counter_cache option instead. Here's the Railscast screencast to get you started.
you are getting that error because starable.total_stars is nil in your callback method. you need to ensure that starable.total_stars is set to 0 ro you can call to_i method on it (nil.to_i #=> 0) to ensure that you have 0 if it is not initialized

Resources