Well, it sure looks like @user
does not contain a User
but rather a ActiveRecord::AssociationRelation::ActiveRecord_AssociationRelation_Status
. No wonder it doesn't have an avatar
method! Check your controller code to see what @user
is actually set to. If you can't make sense of what's going on, post the controller here. The User
model and schema will probably be needed, too.
Polymorphic status associations
-
23-07-2023 - |
Question
I've been developing a web app in Rails 4, and used the Michael Hartl tutorial as the backbone for it, but using Devise as well. After building some of it, I went back and changed the Status model to be polymorphic in order to allow users, venues and groups to be able to post a status. I've got a certain way with this so far, but am now stuck. When trying to view a user's profile, I am currently getting this error:
NoMethodError in Users#show
Showing /home/app_name/app/views/users/show.html.erb where line #6 raised:
undefined method `avatar' for #<ActiveRecord::AssociationRelation::ActiveRecord_AssociationRelation_Status:0xb6859fd8>
Extracted source (around line #6):
<aside class="col-md-4">
<section>
<h1>
<%= image_tag @user.avatar.url(:thumb) %>
<%= @user.name %>
</h1>
</section>
The user definitely has an avatar, and as far as I can work out it seems to be having trouble working out the association through the polynomial relationship. It's on the user#show page, so presumably I shouldn't change "user" to "statusable", I have already tried this anyway, but don't understand where the problem is coming from. The profile shows a feed of statuses at present and an avatar for the user in each.
Any help would be greatly appreciated.
EDIT
Here are my user and status models:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook, :twitter, :google_oauth2]
before_save { self.username = username.downcase }
validates :first_name, presence: true,
length: { maximum: 25 }
validates :last_name, presence: true,
length: { maximum: 25 }
VALID_USERNAME_REGEX = /\A[\w+\-._]+\z/i
validates :username, presence: true,
length: { maximum: 20 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
validates :email, presence: true
validates :password, presence: true
validates :birthday, presence: true
validates :gender, presence: true
validates :postcode, presence: true
validates_format_of :postcode, :with => /\A([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\s?[0-9][ABD-HJLNP-UW-Z]{2}|(GIR\ 0AA)|(SAN\ TA1)|(BFPO\ (C\/O\ )?[0-9]{1,4})|((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ))\z/i, :message => "invalid postcode"
has_attached_file :avatar, :styles => { :medium => "300x300", :thumb => "100x100", :micro => "30x30", :large => "500x500>" }, :default_url => "/images/:style/missing.jpg"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
geocoded_by :address
after_validation :geocode
self.per_page = 20
def address
[town, postcode, country].compact.join(', ')
end
def feed
Status.from_users_favourited_by(self)
end
def self.all_except(user)
where.not(id: user)
end
end
def name
first_name + " " + last_name
end
has_many :statuses, as: :statusable, dependent: :destroy
accepts_nested_attributes_for :statuses
end
class Status < ActiveRecord::Base
before_create :set_latlong
belongs_to :statusable, polymorphic: true
default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :statusable_id, presence: true
validates :statusable_type, presence: true
def set_latlong
self.latitude = statusable.latitude
self.longitude = statusable.longitude
end
def self.from_users_favourited_by(user)
favourited_user_ids = "SELECT favourite_id FROM favouriteusers
WHERE favourited_id = :user_id"
where("statusable_id IN (#{favourited_user_ids}) OR statusable_id = :user_id", user_id: user.id)
end
end
Here is the Status controller:
class StatusController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
before_filter :load_statusable
def new
@status = Status.new(status_params)
end
def create
@statusable = load_statusable
@status = @statusable.statuses.build(status_params)
if @status.save
flash[:success] = "Status created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
def destroy
@status.destroy
redirect_to root_url
end
private
def status_params
params.require(:status).permit(:content)
end
def load_statusable
resource, id = request.path.split('/')[1, 2]
resource_name = resource.singularize.classify
if resource_name = "user"
@statusable = current_user
else
@statusable = resource_name.constantize.find(id)
end
end
end
And finally the status schema:
create_table "statuses", force: true do |t|
t.string "content"
t.integer "statusable_id"
t.string "statusable_type"
t.float "latitude"
t.float "longitude"
t.datetime "created_at"
t.datetime "updated_at"
end
I've taken a few bits out to make it easier to read, but hopefully have left in the bits needed.
EDIT
class UsersController < ApplicationController
before_filter :authenticate_user!, only: [:index, :show,:edit,
:update, :destroy, :favourite_users, :favourited_users]
def index
@users = User.all_except(current_user).paginate(page: params[:page]).order('created_at DESC')
end
def show
@user = User.find(params[:id])
@user = @user.statuses.paginate(page: params[:page])
end
def new
@user = User.new
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
# Handle a successful update.
else
render 'edit'
end
end
def create
@user = User.create( user_params )
end
def favourite_users
@title = "Favourites"
@user = User.find(params[:id])
@users = @user.favourite_users.paginate(page: params[:page])
render 'show_favourite'
end
def favourited_users
@title = "Listed as a Favourite by"
@user = User.find(params[:id])
@users = @user.favourited_users.paginate(page: params[:page])
render 'show_favourite'
end
Solution