Rails has_many through - ruby-on-rails

A user can create organization and then he can make other users as moderators to his organization. Below method shows how the organization is created.
def create
#organization = current_user.organizations.build(organization_params)
# Confirm organization is valid and save or return error
if #organization.save!
# New organization is saved
respond_with(#organization) do |format|
format.json { render :json => #organization.as_json }
end
else
render 'new', notice: "Unable to create new organization."
end
end
How should I create moderators for the organization. I tried using has_many through but it failed. Can somebody help me?
Update
Organization Model
class Organization < ActiveRecord::Base
has_many :moderators
has_many :users, :through => :moderators
end
UserModel
class User < ActiveRecord::Base
enum role: [:user, :moderator, :organization, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :moderators
has_many :organizations, :through => :moderators
end
Moderator Model
class Moderator < ActiveRecord::Base
belongs_to :user
belongs_to :organization
end
When I create new organization my organization user_id is nil?

Take look at has and belongs to many relation http://apidock.com/rails/v4.2.1/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many since one user can be moderator for many organization and organization can have many moderators. Also instead of calling #organization.save! you should call #organization.save because now it will throw error if save would be unsuccessful. You want to have boolean as result of save so your condition works properly

Related

Querying for values from several models

I have a Course and Lesson models. Course has several lessons. I want to find all the lessons for currently logged in student to generate kind of timetable.
I have a method that returns all the courses that this student is studying. Now I want to get all lessons from all those courses in #courses into #lessons, something like:
def index
#courses = current_student.find_courses
#lessons = #courses.lessons
end
Is it possible to do it somehow simple on one line?
The find_courses method is implemented as following:
def find_courses
Course.where("id IN (?)", StudentAssignment.select("course_id").where('student_id == (?)', self.id))
end
The Models:
class Student < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :student_assignments
has_many :courses, :through => :student_assignments
....
class Lesson < ApplicationRecord
belongs_to :room
belongs_to :teacher
belongs_to :course
....
class Course < ApplicationRecord
has_many :lessons, dependent: :destroy
has_many :teacher_assignments
has_many :teachers, :through => :teacher_assignments
has_many :student_assignments
has_many :students, :through => :student_assignments
...
class Student < ApplicationRecord
has_many :courses
def active_lessions
Lession.joins(course: :students).where(students: {id: self.id})
end
end
In this way you can directly get all active lesssions for current_user
current_student.active_lessions
Try:
#lessons = #courses.flat_map(&:lessons)
It takes each course in #courses list and gets the list of lessons for that course.

voting system for rails blog with devise user login before voting

So I have a blog I am trying to have a simple upvote/downvote feature for the posts. I have devise set up and I made all the associations between the models, votings, users, and home_blogs.
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :liked_home_blogs, through: :votings
end
class HomeBlog < ApplicationRecord
mount_uploader :image, ImageUploader
has_many :hashtaggings
has_many :hashtags, through: :hashtaggings
has_many :votings
has_many :votants, through: :votings
def all_hashes=(names)
self.hashtags = names.split(",").map do |name|
Hashtag.where(name: name.strip).first_or_create!
end
end
def all_hashes
self.hashtags.map(&:name).join(", ")
end
end
class Voting < ApplicationRecord
belongs_to :home_blog
belongs_to :user
end
and the controller looks like this at the moment:
class VotingsController < ApplicationController
before_action :authenticate_user!
def upvote
#votings = HomeBlog.find(params[:home_blog_id])
#votings.votings.build( :upvote => true, :downvote => false,
:user_id => current_user.id)
#votings.save!
redirect_to request.referrer, notice: "Thanks for the
vote!"
end
def downvote
#voting = HomeBlog.find(params[:home_blog_id])
#voting.votings.build( :downvote => true, :upvote =>
false, :user_id => current_user.id)
#voting.save!
redirect_to request.referrer, notice: "Thanks for the
vote!"
end
private
def voting_params
params.require(:voting).permit(:upvote, :downvote,
:home_blog_id, :user_id)
end
end
Sorry about the crappy copy and paste for the controller. My question is, how do I make a condition for the current_user in devise to limit them to one vote per home_blog post? Thanks!
I think you would add multicolumn unique index to the join table. Something like...
add_index :voting, [:user_id, :home_blog_id], unique: true
If im understanding your question correctly you would like there to be only one votings record for a home_blog per current_user ( user_id )

Can't create user, "undefined method `save' for nil:NilClass" using Devise in Rails 4

When trying to create a new Person I receive the following error on the line if #persons.save:
NoMethodError in PeopleController#create,
undefined method `save' for nil:NilClass
Any thoughts on how to fix would be much appreciated, thanks.
Controller
# GET /people/new
def new
#person = current_user.person.build
end
# GET /people/1/edit
def edit
end
# POST /people
# POST /people.json
def create
#person = current_user.person.build(person_params)
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render action: 'show', status: :created, location: #person }
else
format.html { render action: 'new' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
User model
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 :person
end
Person model
class Person < ActiveRecord::Base
has_many :user
has_paper_trail
acts_as_taggable
#tags = Person.acts_as_taggable_on :tags
def admin_permalink
admin_post_path(self)
end
end
It seems like you want a relationship where a user can have many people and a person can have many users.
This requires a special association type called has_many through.
Basically a user can be associated to many people and vice versa, :through a third model called a join table.
e.g
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 :people, through: :relationships # The model name(person) is pluralised for has_many associations
end
class Person < ActiveRecord::Base
has_many :users, through: :relationships # user needs to be pluralised here
has_paper_trail
acts_as_taggable
#tags = Person.acts_as_taggable_on :tags
...
end
class Relationship < ActiveRecord::Base # This is the join table
belongs_to :user
belongs_to :person
end
This requires you to create the relationship table in the database (Instead of relationship, call it whatever makes the most sense). It needs person_id and user_id integer columns.
In your controller you will then need to use the pluralised version too:
#person = current_user.people.build(person_params)
You should have a good read of the rails association guide. Particularly the has_many through section.
There is another type of association called a has_and_belongs_to_many which may be better for your case. In my experience it often seems like the easier approach but ends up causing headaches compared to has_many through.

accepts_nested_attributes with 2 nested tables

I am new to Ruby on rails and I am creating simple website. I found out the rails 3 uses attr_accessible while rails 4 uses strong parameters. I am currently working with rails 4. I am not quite sure how to set up the accepts_nested_attributes_for.
This is what I have
User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:rememberable, :validatable
has_many :expense_pictures
has_many :income_pictures
accepts_nested_attributes_for: ExpensePicture ?
accepts_nested_attributes_for: IncomePicture ?
end
ExpensePicture model:
class ExpensePicture < ActiveRecord::Base
belongs_to :user
mount_uploader :image, ImageUploader
end
ExpenseText model:
class ExpenseText < ActiveRecord::Base
belongs_to :expense_pictures
end
IncomePicture model:
class IncomePicture < ActiveRecord::Base
belongs_to :user
mount_uploader :image, ImageUploader
end
IncomeText model:
class IncomeText < ActiveRecord::Base
belongs_to :income_pictures
end
My User controller
class UserController < ApplicationController
def create
User.create(user_params)
accepts_nested_attributes_for :IncomePicture ?
accepts_nested_attributes_for :ExpensePicture ?
end
private
def user_params
# required input for params
# permit - returns a version of the params hash with ony the permitted attributes
params.require(:user).permit(:name, :email, :password, :password_confirmation, ...not sure...)
end
end
Using accepts_nested_attributes_for for has_many relation with your models,your User model should look like this
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:rememberable, :validatable
has_many :expense_pictures
has_many :income_pictures
accepts_nested_attributes_for :expense_pictures
accepts_nested_attributes_for :income_pictures
end
Your new and create methods of usercontroller should look like this
def new
#user = User.new
#user.expense_pictures.build
#user.income_pictures.build
end
def create
#user = User.new(user_params)
if #user.save
redirect_to #user
else
render 'new'
end
end
And your user_params method should look something like this
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, expense_pictures_attributes: [:your_attr1,:your_attr2,..],income_pictures_attributes: [:your_attr1,:your_attr2,..])
end
Feel free to ask if you want any more information regarding to this.

Undefined method `each' for #<User:

Rails 4.1
Ruby 2.0
Credential.rb
class Credential < ActiveRecord::Base
belongs_to :category
has_many :user
validates :name, :login, :password, presence: true
attr_accessor :encryption_key
attr_encrypted :login, key: :encryption_key
attr_encrypted :password, key: :encryption_key
end
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 :credentials
def you
"You are <b>#{email}</b>"
end
end
CredentialsController.rb
class CredentialsController < ApplicationController
before_filter :authenticate_user!
def create
#credential = current_user.credentials.new
#credential.encryption_key = session[:master_key]
#credential.update(credential_params)
if #credential.save
redirect_to credential_path(#credential), notice: "Password entry created successfully."
else
render "new"
end
end
The line:
#credential.update(credential_params)
throws an exception
undefined method 'each' for #<User:0x4de4f58>
You need to edit your associations. You have credentials that has_many :user and users that has_many :credentials. The one with the foreign key should be a belongs_to not has_many. If you're attempting to make a many-to-many relationship, then either use has_many_and_belongs_to or a join table. Further, it should be has_many :users and not has_many :user. That should resolve your error.

Resources