Rails has_one and belongs_to help - ruby-on-rails

I have two models: User and Store
class Store < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_one :store
Schema looks like this:
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "encrypted_password"
t.string "salt"
t.boolean "admin", :default => false
t.string "username"
t.string "billing_id"
end
create_table "stores", :force => true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "store_name"
t.integer "user_id"
end
User must login in order to sign up a store by inputting "email" and "store_name". create from stores_controller looks like this:
def create
#store = Store.new(params[:store])
if #store.save
#store.user_id = current_user.id
flash[:success] = "this store has been created"
redirect_to #store
else
#title = "store sign up"
render 'new'
end
end
In ApplicationsController
def current_user
#current_user ||= user_from_remember_token
end
However, when I check in the database, #store.user_id = nil. For some reason, it's not able to put in current_user.id into #store.user_id. Anybody able to help in detecting why this might be? I thought I had associations correctly implemented. Thanks

This is happening because you're setting the #store.user_id AFTER saving it.
Ideally, you should be using the association builder for this:
def new
#store = #current_user.store.build(params[:store])
More information about these can be found in the "Association Basics" guide.

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"

How to add relation to existing object in Rails

I want to add relation between existing post and author.
I was trying to modify created_by attribute but it's not accessible from object.
def set_author
if (#post.created_by.empty? && #post.author_code.present?)
if #post.author_code == params[:author_code]
#post.created_by = current_user
else
raise(ExceptionHandler::InvalidAuthorCode, Message.invalid_author_code)
end
else
raise(ExceptionHandler::DisallowedAction, Message.action_not_allowed)
end
end
It's not working because there is no method #post.created_by even if it's present db.
Post model from schema.rb
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.boolean "accepted", default: false
t.string "created_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "author_code"
end
Edit:
part of post.rb
belongs_to :user, optional: true, foreign_key: :created_by
part of user.rb
has_many :confessions, foreign_key: :created_by
Assuming that it only fails for the #post which don't have any created_by OR user then you can use following:
#post.try(:created_by).blank? && #post.author_code.present?
I was trying to update #post attributes a wrong way. Changed
#post.created_by = current_user
to
#post.update_attribute(:created_by, current_user)
and it's working.
It's not a Devise user model, just self written.

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

Finds in Rails 3 and ActiveRelation

I'm trying to understand the new arel engine in Rails 3 and I've got a question.
I've got two models, User and Task
class User < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :user
end
here is my routes to imply the relation:
resources :users do
resources :tasks
end
and here is my Tasks controller:
class TasksController < ApplicationController
before_filter :load_user
def new
#task = #user.tasks.new
end
private
def load_user
#user = User.where(:id => params[:user_id])
end
end
Problem is, I get the following error when I try to invoke the new action:
NoMethodError: undefined method `tasks' for #<ActiveRecord::Relation:0x3dc2488>
I am sure my problem is with the new arel engine, does anybody understand what I'm doing wrong?
Sorry guys, here is my schema.db file:
ActiveRecord::Schema.define(:version => 20100525021007) do
create_table "tasks", :force => true do |t|
t.string "name"
t.integer "estimated_time"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
create_table "users", :force => true do |t|
t.string "email", :default => "", :null => false
t.string "encrypted_password", :limit => 128, :default => "", :null => false
t.string "password_salt", :default => "", :null => false
t.string "reset_password_token"
t.string "remember_token"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "username"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
add_index "users", ["username"], :name => "index_users_on_username", :unique => true
end
I believe you want:
def load_user
#user = User.where(:id => params[:user_id]).first
end
Until you ask for a record it will stay a relation.
But find(params[:user_id]) will still work and return the record.
Does it work if you change your load_user method as shown below?
def load_user
#user = User.find(params[:user_id])
end
Also, I think you might need to change your new action to:
def new
#task = #user.tasks.build
end
Don't confuse the Arel gem's interface with the new ActiveRecord query interface. The syntax described here will not work: http://github.com/brynary/arel
ActiveRecord uses Arel under the hood but creates its own Arel-like API. Here's a brief look at the new ActiveRecord query interface: http://m.onkey.org/2010/1/22/active-record-query-interface
It's actually pretty simple. Here is one method...
def new
#task = #user.tasks.new
end
private
def load_user
# you must call a non-deferred operator to actually return
# the tuple that is connected to your tasks
#user = User.where(:id => params[:user_id]).first
end
Be sure to take a look at the seven part learning series I am doing on Active Relation. The first episode will help you understand what your error was more clearly. http://Innovative-Studios.com/#pilot
find() IS NOT deprecated in some instances as stated before. I would limit the use of find() for atomic values (where you are searching for a specific item/index) Anything that could possibly be collection based I would stick to the Where (restriction) clause wrapper in ActiveRecord for Arel.

Initializing "new users" in Rails

I'm creating a Ruby on Rails application, and I'm trying to create/login/logout users.
This is the schema for Users:
create_table "users", :force => true do |t|
t.string "first_name"
t.string "last_name"
t.text "reputation"
t.integer "questions_asked"
t.integer "answers_given"
t.string "request"
t.datetime "created_at"
t.datetime "updated_at"
t.string "email_hash"
t.string "username"
t.string "hashed_password"
t.string "salt"
end
The user's personal information (username, first/last names, email) is populated through a POST. Other things such as questions_asked, reputation, etc. are set by the application, so should be initialized when we create new users. Right now, I'm just setting each of those manually in the create method for UsersController:
def create
#user = User.new(params[:user])
#user.reputation = 0
#user.questions_asked = 0
#user.answers_given = 0
#user.request = nil
...
end
Is there a more elegant/efficient way of doing this?
params[:user] is just a hash, you could create a hash and merge it with the params like
def create
params[:user].merge(
{
:reputation => 0,
:questions_asked => 0,
:answers_given => 0
...
}
)
#user = User.new(params[:user])
end
You could move this to your model if you wanted to remove that code from your controller, and just add an after_create filter..
but really if its just setting things to 0, set defaults in the database columns and you wont even have to handle it in your code..
create_table "users", :force => true do |t|
t.string "first_name"
t.string "last_name"
t.text "reputation", :default => 0
t.integer "questions_asked", :default => 0
t.integer "answers_given", :default => 0
t.string "request"
t.datetime "created_at"
t.datetime "updated_at"
t.string "email_hash"
t.string "username"
t.string "hashed_password"
t.string "salt"
end
If you cannot redo your migration, use change_column_default like
class SetDefaultsOnUsers < ActiveRecord::Migration
def self.up
change_column_default "users", "reputation", 0
end
def self.down
change_column_default "users", "reputation", default
end
end
You can use http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#M001909

Resources