Uninitialized constant error using a relationship - ruby-on-rails

I followed the instructions here to create a model Lesson in which there is a student and a teacher (both of the model User) and also a lesson start date.
#Lesson Controller
class Lesson < ActiveRecord::Base
belongs_to :student, class_name => 'User'
belongs_to :teacher, class_name => 'User'
end
#User Controller
class User < ActiveRecord::Base
has_many :lessons_to_attend, :class_name => 'Lesson', :foreign_key => 'student_id'
has_many :lessons_to_teach, :class_name => 'Lesson', :foreign_key => 'teacher_id'
end
The migration went smoothly and so on a page I try to query the student's lessons for tomorrow:
<% #date = 1.day.from_now %>
<%= #date.strftime("%A")%></br>
<%= #date.strftime("%-d/%-m/%y")%>
<% #user.lessons_to_attend.each do |l| %>
Lesson
<% end %>
But when I navigate to this page I get the error Uninitialized constant error Lesson::User
What did I miss out? I'll include the User controller in case something needs to be added in there.
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
# GET /users/1
# GET /users/1.json
def show
#user = User.find(params[:id])
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params[:user]
end
end

Two things:
belongs_to :student, class_name => 'User'
belongs_to :teacher, class_name => 'User'
Syntax error on class_name. That should either be :class_name => 'User' or class_name: 'User'.
The other thing is that I think you need to set your inverse_of on both sides of the association.
class Lesson < ActiveRecord::Base
belongs_to :student, class_name: 'User', inverse_of: :lessons_to_attend
belongs_to :teacher, class_name: 'User', inverse_of: :lessons_to_teach
end
class User < ActiveRecord::Base
has_many :lessons_to_attend, class_name: 'Lesson', foreign_key: 'student_id', inverse_of: :student
has_many :lessons_to_teach, class_name: 'Lesson', foreign_key: 'teacher_id', inverse_of: :teacher
end

Related

How to clean code and eliminate the NoMethodError exception

When a user creates an accounts, the code should allow that user to register a user account through Devise and then create an account, plus a company.
The code should redirect to a wizard setup using the wicked gem.
Users have roles (rolify) and are authorized with Cancancan.
The tables are setup using has_many :through association. This seems to work.
The models are as follows:
user
has_many :accounts_users
has_many :accounts, through: :accounts_users, dependent: :destroy
has_one :company, through: :accounts, dependent: :destroy
users_account
belongs_to :account
belongs_to :user
account
resourcify
has_many :accounts_users
has_many :users, through: :accounts_users, dependent: :destroy
has_one :company
company
belongs_to :account
The create account controller is the following:
def new
#account = Account.new
end
def create
if !current_user.present?
build_resource(sign_in_params)
account = Account.find_or_create_by(org_name: params[:user] [:account])
else
#account = current_user.accounts.create!(account_params)
end
# create a default account and company
#account.save!
current_user.add_role 'owner'
current_account = current_user.accounts.first
# create a company associated with the newly created account
if current_account.companies.empty?
company = current_account.companies.create(company_name: params[:org_name])
else
company = current_account.companies.first
end
resource.update(current_company: company.id)
respond_to do |format|
if #account.save
# redirect to the relevant wizard
format.html { redirect_to after_account_path(:add_account), notice: 'Account was successfully created.' }
else
format.html { render action: 'new' }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
EDITED VERSION
def create
#account = Account.create(account_params.except(:company))
#account.save!
respond_to do |format|
if #account.save
create_company
format.html { redirect_to #account, notice: 'Account was successfully created.' }
format.json { render :show, status: :created, location: #account }
else
format.html { render :new }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
def create_company
current_user.add_role 'owner'
current_account = current_user.accounts.first
if current_account.company.nil?
current_account.build_company(company_name: params[:org_name])
#company.save!
else
company = current_account.company
end
resource.update(current_company: company.id)
end
included in Application helper:
def current_account
current_user.accounts.first
end
def current_company
current_user.accounts.first.companies.first
end
it redirects (unexpected) to displaying (show) the Company data right after creation, I receive a nil / no method error.
the index and how controller are:
before_action :set_company, only: [:show, :edit, :update, :destroy,
:new]
def index; end
def show
#company = Company.find_by(id: params[:id])
end
def set_company
#company = current_account.company
end
This seems to work. Editing the Company is a challenge but should get there.
Any advices to get to a better code is welcome
Have you thought about just making the front end handle the case where #name isn't set yet?
<=% #company&.name %>
or
<=% #company.name || "Unknown" %>

SQLite3::SQLException: no such column

I'm trying to build an app where people can specify what posts need to be read before this one and here I'm trying to show them.
<%= #post.before.each do |before| %>
<li><%= before.title %></li>
<% end %>
This gets me this error:
SQLite3::SQLException: no such column: connections.post_id: SELECT "posts".* FROM "posts" INNER JOIN "connections" ON "posts"."id" = "connections"."after_id" WHERE "connections"."post_id" = ?
There isn't supposed to be a post_id column in connections, there is supposed to be an before_id column in it.
My models are:
post.rb:
class Post < ApplicationRecord
has_many :connections
has_many :before, through: :connections, source: :after, foreign_key: "before_id"
has_many :after, through: :connections, source: :before, foreign_key: "after_id"
end
connection.rb
class Connection < ApplicationRecord
belongs_to :before, class_name: "Post", foreign_key: "before_id"
belongs_to :after, class_name: "Post", foreign_key: "after_id"
end
The controller code for posts - automatically generated with scaffolding:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
def new
#post = Post.new
end
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:title, :text)
end
end
has_many :connections in your Post model is telling Rails that you have a post_id on the connections table. If this is not the case, I recommend deleting the association or defining a foreign_key for the association.
I would recode your model as follows:
class Post < ApplicationRecord
has_many :before, class_name: 'Connection', foreign_key: "before_id"
has_many :after, class_name: 'Connection', foreign_key: "after_id"
end
NOTE: You have source: :before and source: :after set opposite their respective association names and foreign_key names. I suspect that was not intentional. However, if it was intentional, you may want to swap foreign_keys between your two associations (i.e put the before_id on the after association and vis versa).

error while adding tags to a post in rails

I wanted to add tags to my products in rails project, so i watched a youtube video how to do it (https://www.youtube.com/watch?v=rzx5MrCa0Pc&t=254s)
I did all he did, but i when i add a new product i get an error -
'New Product
1 error prohibited this product from being saved:
User must exist' , right above my new product form
how do i fix it.
MY ROUTES
Rails.application.routes.draw do
devise_for :users, :controllers => { :registrations => "registrations"}
resources :products
get 'home/ContactUs'
get 'home/Login'
get 'home/Store'
get 'home/blogs'
get 'home/index'
resources :home
root 'home#index'
MY PRODUCT MODEL
class Product < ActiveRecord::Base
belongs_to :user
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
def self.tagged_with(name)
Tag.find_by!(name: name).products
end
def all_tags=(names)
# names="music, spotify"
self.tags = names.split(',').map do |name|
Tag.where(name: name).first_or_create!
end
end
def all_tags
tags.map(&:name).join(", ")
end
end
TAG MODEL
class Tag < ApplicationRecord
has_many :taggings, dependent: :destroy
has_many :products, through: :taggings
end
TAGGINGS MODEL
class Tagging < ActiveRecord::Base
belongs_to :product
belongs_to :tag
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 :products
end
PRODUCT_CONTROLLER
class ProductsController < ApplicationController
# before_action :authenticate_user!
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
if params[:tag]
#products = Product.tagged_with(params[:tag])
else
#products = Product.all
end
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:filetype, :title, :img_url, :description, :all_tags, :price, :uploaded_by, :tutorial_url)
end
end
User must exist
The problem is Product belongs to User but you are trying to create the product without passing user_id which will create an orphan product
Solution: Change
def create
#product = Product.new(product_params)
to
def create
#product = current_user.products.new(product_params)
Also, You will need to change set_product method to make sure you can update or delete only products created by user
def set_product
#product = current_user.products.find(params[:id])
end

How to send a mailer on update of an object in a HABTM join table

I have a User object and an Orgs object that are associated through a HABTM join table. I want to send an email to the users when the Orgs object is updated AND the Org.approved value is set to true. I have an approved boolean on the Org.
I think I've gotten most of the way there but I need help with the step of actually sending the email.
Here's my code
class OrgMailer < ApplicationMailer
default from: 'myemail#example.co'
def org_approved(user, org)
#user = user
#orgs = User.orgs.all
#url = 'http://example.com/login'
mail(to: #user.email, subject: 'Your listing has been approved.')
end
end
User.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_and_belongs_to_many :orgs, join_table: :orgs_users
end
Org.rb
class Org < ApplicationRecord
has_and_belongs_to_many :users, join_table: :orgs_users
# after_update :send_approved_listing_email, only: [:update]
attachment :company_image
def send_approved_listing_email
OrgMailer.org_approved(i).deliver_now if org.approved === true
end
end
UPDATED: ADDED ORG_CONTROLLER
I've edited my code to look like the answer below but am now getting a new error: uninitialized constant Org::OrgsUser
It's caused when I hit the #org.users << #user line in the create action.
If I delete this line, I'm able to create an org but it's not associating properly.
org_controller.rb
class OrgsController < ApplicationController
before_action :set_org, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#orgs = Org.all
#tags = ActsAsTaggableOn::Tag.all
end
def show
end
def new
#org = Org.new
end
def contest
end
def edit
end
def create
#user = current_user
#org = Org.new(org_params)
#org.users << #user
respond_to do |format|
if #org.save
format.html { redirect_to thankyou_path, notice: 'Your listing was successfully created. Our team will approve your listing after review.' }
format.json { render :show, status: :created, location: #org }
else
format.html { render :new }
format.json { render json: #org.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #org.update(org_params)
format.html { redirect_to #org, notice: 'Listing was successfully updated.' }
format.json { render :show, status: :ok, location: #org }
else
format.html { render :edit }
format.json { render json: #org.errors, status: :unprocessable_entity }
end
end
end
def destroy
#org.destroy
respond_to do |format|
format.html { redirect_to orgs_url, notice: 'Listing was successfully destroyed.' }
format.json { head :no_content }
end
end
def tagged
if params[:tag].present?
#orgs = Org.tagged_with(params[:tag])
else
#orgs = Org.postall
end
end
private
def set_org
#org = Org.find(params[:id])
end
def org_params
params.require(:org).permit(:twitter, :linkedin, :facebook, :name, :offer, :offercode, :url, :descrption, :category, :approved, :company_image, :tag_list => [])
end
end
I'm using active admin for my admin panel and have a batch action to update any selected orgs and approve them. I think what I'm missing is that in the send_approved_listing_email method I need to iterate through the orgs and email each user when the org is approved.
Right now nothing happens on update so I'm sure I'm not doing this correctly. What am I missing? How should I write this?
I would create a model for the join table rather than using habtm. That way you can use a callback when the join object is saved:
class User < ApplicationRecord
has_many :orgs_users
has_many :orgs, through: :orgs_users
end
class Org < ApplicationRecord
has_many :orgs_users
has_many :users, through: :orgs_users
end
class OrgsUsers < ApplicationRecord
belongs_to :org
belongs_to :user
after_create :send_approved_listing_email
def send_approved_listing_email
OrgMailer.org_approved(user, org).deliver_now if org.approved === true
end
end

Rails Has_Many :Through confusion

I'm working on creating a basic survey app as I'm learning rails. I've setup a has_many through relationship between the surveys as the questions (as questions may be used in multiple surveys). I've been struggling with adding a question to a survey though. Any idea what I need to do to create a new surveytization when creating my new question (and thus adding the question to the survey)? I'm able to do it in the console but am struggling with translating that to the controllers/views/params - if you know of any good documentation about those I'd love to check them out to (but thus far haven't found much).
It seems to error out when I try to assign my #survey variable using the :survey_id in the params I'm sending to the Question controller.
I really appreciate your help!
Question.rb:
class Question < ActiveRecord::Base
has_many :answers, dependent: :delete_all
validates :title, presence: true
has_many :surveytizations
has_many :surveys, :through => :surveytizations
end
Survey.rb
class Survey < ActiveRecord::Base
has_many :surveytizations
has_many :questions, :through => :surveytizations
end
Surveytization.rb:
class Surveytization < ActiveRecord::Base
has_many :surveys
has_many :questions
validates :survey_id, presence: true
validates :question_id, presence:true
end
SurveyController.rb:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
before_action :set_question
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
end
# GET /surveys/1/edit
def edit
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render action: 'show', status: :created, location: #survey }
else
format.html { render action: 'new' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url }
format.json { head :no_content }
end
end
def add_question(question)
surveytizations.create!(question_id: question.id)
end
def remove_question(question)
surveytizations.find_by(question_id: question.id).destroy
end
def find_question(question)
#question = surveytizations.find_by(question_id: question.id)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
def set_question
#question = Question.new
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:title)
end
end
Survey show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #survey.title %>
</p>
<%= link_to 'Edit', edit_survey_path(#survey) %> |
<%= link_to 'Back', surveys_path %>
<%= link_to "Add Question", new_question_path(:survey_id => #survey.id)%>
QuestionController:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
#answers = #question.answers
end
# GET /questions/new
def new
#question = Question.new
#survey = Survey.find(:survey_id)
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :single_response, :surveytization)
end
end
One problem is your join relation should have belongs_to instead of has_many, to get the has_many through working:
class Surveytization < ActiveRecord::Base
belongs_to :survey
belongs_to :question
validates :survey_id, presence: true
validates :question_id, presence:true
end
Notice the :survey and :question are singular name in the belongs_to
To Add a question to a survey you can
# create new question or find existing question and store it in #question
#question
# get the survey into #survey
#survey
#survey.questions << #question
This will magically create the surveytization as well. Now that question will belong to that survey.
You Don't even have to call #survey.save! after.

Resources