I'm a Rails beginner and I'm currently adding some basic associations to my models. I have the following in my two models:
User model:
class User < ActiveRecord::Base
has_many :photos, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_uniqueness_of :username
end
Photos model:
class Photo < ActiveRecord::Base
belongs_to :user
has_attached_file :image,
styles: { medium: '300x300>', thumb: '100x100>' },
default_url: '/images/:style/missing.png'
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
After adding the above, I ran the appropriate migrations:
rails g migration AddUserIdToPhotos user_id:integer
rake db:migrate
The user_id was added to the Photos table but when I add a new photo, the user_id field is set to nil.
When I went to psql to double check there, it does not even show nil, just an empty field.
This is the code in the Photos Controller:
def create
photo = Photo.new(photo_params)
if photo.save
Pusher['the_force'].trigger('new_photo', {
url: photo.image.url(:medium),
description: photo.description,
id: photo.id
})
end
redirect_to '/'
end
private
def photo_params
params.require(:photo).permit(:image, :description, :user_id)
end
end
Any help here would be much appreciated.
If you are not passing the user from your form and you wants to save the user that is already logged in, you have to pass it when creating the photo instance.
photo = Photo.new(photo_params, :user => current_user)
Related
I'm trying to figure out how to write a method in my user model, that shows the user's full name and the title of the organisation that the user is from, in one line.
In my user model, I have a method called full_name, which works to add the first name and last name attributes together. That part works.
Now, I'm trying to write a method called 'formal_title', which adds the user's full name to an organisation.title.
I have models for User and Organisation. The associations are:
User
belongs_to :organisation
Organisation
has_many :users
My organisation table has an attribute called :title.
My attempt at writing a method in my user model is:
def full_name
if first_name.present?
[*first_name.capitalize, last_name.capitalize].join(" ")
else
test_full_name
end
end
def organisation_title
Organisation.title.where(organisation.id == self.organisation_id).titleize
end
def formal_title
[*self.full_name, self.organisation_title].join(",")
end
When I try this, I get an error in the console that says:
NoMethodError: undefined method `formal_title' for #<User:0x007fea3495fe90>
This error message makes no sense to me because the user that I'm testing this on has a first name, last name and organisation id to test against. the organisation_id in the user table references an organisation that has a title, so I'm lost as to why any part of this method could be missing but also confused about what the console doesn't know about the method.
Can anyone see what I've done wrong?
Entire user model has:
class User < ApplicationRecord
rolify strict: true # strict means you get true only on a role that you manually add
attr_accessor :current_role
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable,
:omniauthable, :omniauth_providers => [ :linkedin, :twitter, :orcid ]
# --------------- associations
belongs_to :organisation, optional: true
has_one :device
has_many :identities, dependent: :destroy
has_one :profile, dependent: :destroy
has_one :setting, dependent: :destroy
has_one :org_request, dependent: :destroy
has_many :proposals, dependent: :destroy
# Conversations
has_many :authored_conversations, class_name: 'Conversation', foreign_key: 'author_id'
has_many :received_conversations, class_name: 'Conversation', foreign_key: 'received_id'
has_many :personal_messages, dependent: :destroy
# End Conversations
# teams
has_many :teams, foreign_key: "team_mate_id"
has_many :team_projects, through: :teams, source: :proposal
has_many :received_invitations, :class_name => "TeamInvitation", :foreign_key => 'recipient_id'
has_many :sent_invitations, :class_name => "TeamInvitation", :foreign_key => 'sender_id'
# --------------- scopes
# --------------- validations
validates :first_name, presence: { message: 'First name cannot be blank.' }
validates :last_name, presence: { message: 'Last name cannot be blank.' }
validates :email, format: { with: EMAIL_REGEX }
validates :email, uniqueness: { message: 'This email is already associated with an account. Try logging in.'}
# validates_format_of :first_name, with: /a-zA-Z/
# validates_format_of :last_name, with: /a-zA-Z/
# validates_length_of :password, within: 8..128
# --------------- class methods
def full_name
if first_name.present?
[*first_name.capitalize, last_name.capitalize].join(" ")
else
"test_full_name"
end
end
def organisation_title
# you don't need to do `Organisation.where...` here because you have defined the `belongs_to` association for `:organisation`. So, it will directly give you the `Organisation` object.
organisation.title.titleize
end
def formal_title
[*self.full_name, self.organisation_title].join(",")
end
# --------------- instance methods
def online?
true
# false
# !Redis.new.get("user_#{self.id}_online").nil?
end
end
I save my changes each time. then I reload! in the console.
Not enough. You should either restart the console or re-query the user.
u = User.find(u.id)
reload! doesn't affect already loaded objects, so the user you had there, it won't see the new method.
Please try to change your code like this and let me know what it gives:
def full_name
if first_name.present?
[first_name.capitalize, last_name.capitalize].join(" ")
else
test_full_name # will work if test_full_name is defined inside this class
end
end
def organisation_title
# you don't need to do `Organisation.where...` here because you have defined the `belongs_to` association for `:organisation`. So, it will directly give you the `Organisation` object.
organisation.title.titleize
end
def formal_title
[self.full_name, self.organisation_title].join(",")
end
I have setup a rails application with devise on it so users can register and login and they can add posts and other resources based on their roles(Admin,Teacher,Author,Pupil). Admin as access to everything and I am using gem 'cancan' for roles.
if user.role
if user.role.name == "Admin"
can :manage, :all
elsif user.role.name == "Teacher"
can :manage, [Book, Story]
can :read, [Book, Author, Story]
elsif user.role.name == "Author"
can :manage, [Book, Author]
can :read, [Book, Author, Story]
elsif user.role.name == "Pupil"
can :manage, [Story]
can :read, [Book, Author, Story]
elsif user
can :read, :all
end
else
can :read, :all
end
Currently Authors are just added to a book when you create the book. In the backend you just add authors as many as you like and then assign that to a book. If your an admin you can edit authors details and if your an author you can edit your own profile area in your author resource but there is no login for an author to do this at all.
What I want to know is: Is it easy to create a devise user type like author from authors already added to the backend or will they need to be created all over again?
=== Edit below here ====
user.rb
class User < ActiveRecord::Base
belongs_to :role
def confirmation_required?
false
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_attached_file :avatar, styles: {
large: "600x450#",
medium: "250x250#",
small: "100x100#"
}, :default_url => "/images/:style/filler.png"
#validates_attachment_content_type :avatar, :content_type => ["image/jpg", "image/jpeg", "image/png", "image/gif"]
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates :avatar, :email, :username, :password, presence: true
end
role.rb
class Role < ActiveRecord::Base
has_many :users
end
author.rb
class Author < ActiveRecord::Base
has_many :books, dependent: :destroy
has_many :ideas, dependent: :destroy
accepts_nested_attributes_for :books
accepts_nested_attributes_for :ideas
validates_uniqueness_of :name
has_attached_file :avatar, :styles => { :large => "980x760", :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end
Thanks
Mark
You don't need to create separate model(Author) just to handle author logins.
Better approach will be using associations in this case.
#user_role.rb
class UserRole < ActiveRecord::Base
has_many :users
end
#user.rb
Class User < ActiveRecord::Base
belongs_to :user_role
end
Typical user model attributes will be
User(id: integer, email: string, name:string, user_role_id:integer)
user_role model attributes will be
UserRole(id: integer, role: string)
UserRole.create(role: 'Admin')
UserRole.create(role: 'Teacher')
UserRole.create(role: 'Author')
UserRole.create(role: 'Pupil')
then create your author( Let's assume the id for 'Author' role is 3)
user = User.create(name: 'abc', email: 'def#gmail.com', user_role_id: 3)
now, use this user object and go for devise login.
Now to list all the authors of our Application.
#user.rb
def self.all_authors
User.select('users.id, users.name, user_roles.role AS USER_ROLE')
.joins(:user_role).where(user_roles: {role: 'Author'})
end
Now to get all authors just give a call :
User.all_authors #this will list all the users of type 'author'
In my rails application I have an upvote system, it allows people to upvote a pin. Then when a user upvoted a pin I render a list of upvoted pins in the user profile. What I want to do know is to provide a list of the users who upvoted a specific pin.
app/controller/pins_controller.rb
def upvote
#pin = Pin.find(params[:id])
if #pin.votes.create(user_id: current_user.id)
flash[:notice] = "Thank you for upvoting! You can upvote a startup only once."
redirect_to(pins_path)
else
flash[:notice] = "You have already upvoted this!"
redirect_to(pins_path)
end
end
app/controller/users_controller.rb
def show
#user = User.find(params[:id])
#pins_for_user = []
#user.votes.each do |vote|
#pins_for_user << vote.pin
end
end
app/models/pin.rb
class Pin < ActiveRecord::Base
belongs_to :user
has_many :votes, dependent: :destroy
has_many :upvoted_users, through: :votes, source: :user
has_many :rewards, dependent: :destroy
has_many :rewarded_users, through: :rewards, source: :user
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
has_many :votes, dependent: :destroy
has_many :upvoted_pins, through: :votes, source: :pin
has_many :rewards, dependent: :destroy
has_many :rewarded_pins, through: :rewards, source: :pin
end
app/models/vote.rb
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :pin, counter_cache: true
validates_uniqueness_of :pin_id, scope: :user_id
end
I was thinking of using #pin.upvoted_users to provide this list but I didn't succeeded to implement it correctly, any ideas?
I have a rails app with a basic set up to allows users to upvote pins, those pins are ordered from the most upvoted to the less upvoted. What I would like to do now is to render the lists of pins that a user upvoted on his profile.
Here is my config:
app/controllers/pins_controllers.rb
def upvote
#pin = Pin.find(params[:id])
if #pin.votes.create(user_id: current_user.id)
flash[:notice] = "Thank you for upvoting! You can upvote a startup only once."
redirect_to(pins_path)
else
flash[:notice] = "You have already upvoted this!"
redirect_to(pins_path)
end
end
app/models/pin.rb
class Pin < ActiveRecord::Base
belongs_to :user
has_many :votes, dependent: :destroy
has_many :upvoted_users, through: :votes, source: :user
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
app/models/user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
has_many :votes, dependent: :destroy
has_many :upvoted_pins, through: :votes, source: :pin
end
app/models/vote.rb
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :pin, counter_cache: true
validates_uniqueness_of :pin_id, scope: :user_id
end
And my routes.rb
resources :pins do
member do
post 'upvote'
end
end
Do you have any ideas how I could do that?
You can get the pins a user #user upvoted for example by the following:
#pins_for_user = []
#user.votes.each do |vote|
#pins_for_user << vote.pin
end
You can embedd this in your user controller, for example in the show method.
Then you can refer to #pins_for_user in your show view (show.html.erb) and display it by:
<% #pins_for_user.each do |pin| %>
<%= pin.name %> # or any other code to display that special pin
<% end %>
I have a Post model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :tag_names
belongs_to :user
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
def tag_names
#tag_names || tags.map(&:name).join(' ')
end
private
def assign_tags
if #tag_names
self.tags = #tag_names.split(" ").map do |name|
Tag.find_or_create_by_name(name)
end
end
end
end
a Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
has_many :subscribed_users, :source => :user, :through => :subscriptions
end
and an User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :avatar
has_many :posts, :dependent => :destroy
has_many :subscriptions
has_many :subscribed_tags, :source => :tag, :through => :subscriptions
end
posts and tags have a many-to-many relationship (the following is the model for the join table):
class Tagging < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
users and tags have also a many-to-many relationship:
class Subscription < ActiveRecord::Base
belongs_to :user
belongs_to :tag
end
Only posts with tags that the user has subscribed to should display:
def index
#title = "Posts"
#posts = current_user.subscribed_tags.map(&:posts).flatten.paginate(:page => params[:page], :per_page => 5)
Let say I create a tag for a post:
$ post.tags.create(:name => "food")
$ post.tags
=> [#<Tag id: 6, name: "food", created_at: "2012-03-02 10:03:59", updated_at: "2012-03-02 10:03:59"]
Now I have no idea how to subscribe the user to that tag.
I tried this:
$ user.subscribed_tags.create(:name => "food")
$ post.tags
=> [#<Tag id: 7, name: "food", created_at: "2012-03-02 10:04:38", updated_at: "2012-03-02 10:04:38"]
But as you can see it actually creates a new tag instead of adding the food tag with ID 6 to the user.subscribed_tags attribute.
Any suggestions to solve this issue?
You can append to the user's subscriped_tags, as you would do an array.
ex: user.subscribed_tags << Tag.find_by_name("food")