ActiveRecord::HasManyThroughNestedAssociationsAreReadonly Exception: Cannot modify association - ruby-on-rails

NOTE:
Yes, I know that there is supposedly duplicate posts on this matter, but the latest one that I saw is from 10 years ago (Link to that post), was never solved, and none of the posts had gotten an answer that could solve my issue, thus here I am...
I have been trying to make an mock website where an User can make Binders, that hold Decks, which hold Flashcards.
My problem here is (most likely) with my joins table, the Flashcard. Flashcard is my joins table because I am trying to also create an page where an User can browse through all of the Flashcards made by every User, and add it to one of their Decks (basically, shared access).
Every time I try to create an Flashcard, I get this error:
ActiveRecord::HasManyThroughNestedAssociationsAreReadonly Exception: Cannot modify association 'User#flashcards' because it goes through more than one other association.
Here is my code:
User model:
class User < ApplicationRecord
has_secure_password
has_many :binders
has_many :decks, through: :binders
has_many :flashcards, through: :decks
end
Binder model:
class Binder < ApplicationRecord
belongs_to :user
has_many :decks
has_many :flashcards, through: :decks
end
Deck model:
class Deck < ApplicationRecord
belongs_to :binder
has_many :flashcards
end
Flashcard model:
class Flashcard < ApplicationRecord
belongs_to :user
belongs_to :deck
validates :user, :deck, presence: true
end
flashcards_controller.rb:
class FlashcardsController < ApplicationController
def create
flashcard = set_user.flashcards.new(flashcard_params)
flashcard.user_id = user.id
if flashcard.save
render json: flashcard, status: :created
else
render json: {errors: flashcard.errors.full_messages}, status: :unauthorized
end
end
private
def flashcard_params
params.require(:flashcard).permit(:id, :user_id, :deck_id, :question, :answer) # Verify if the user_id is needed from frontend
end
end
My schema.rb:
create_table "binders", force: :cascade do |t|
t.string "name"
t.integer "user_id"
end
create_table "decks", force: :cascade do |t|
t.integer "binder_id"
t.string "name"
end
create_table "flashcards", force: :cascade do |t|
t.integer "user_id"
t.integer "deck_id"
t.string "question"
t.string "answer"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "password_digest"
end

Okay, so I figured out how to fix this issue...
My updated flashcards_controller.rb create method:
def create
deck = current_user.decks.find_by(id: params[:deck_id])
flashcard = deck.flashcards.new(flashcard_params)
flashcard.user_id = current_user.id
if flashcard.save
render json: flashcard, status: :created
else
render json: {errors: flashcard.errors.full_messages}, status: :unauthorized
end
end
This new create method works, because after the Flashcard object is created (but before attempting to save it), the method will keep checking for the new Flashcard object until it is found, assigns the user_id to it, and then attempts to save the new object...

Related

How do I convert a SQL query to rails?

I'm new to ROR. The query I created below is functioning properly. I'm trying to prepare an interrogation. But I didn't succeed. The query is as follows;
#rad_user_group.groupname = SELECT groupname FROM radgrs INNER JOIN nas WHERE radgrs.grdefault = true AND radgrs.tenant_id = nas.tenant_id AND nas.realipaddr = "192.168.22.175" AND nas.active = true
How do I make a switch case for the following queries in another question? Returns NULL if the query fails.
Thank you for caring.
def realipaddr
request.remote_addr
end
def create
#rad_check = RadCheck.new(rad_check_params)
#rad_check.tenant_id = Na.find_by(realipaddr: realipaddr, active: :true).tenant_id
respond_to do |format|
if #rad_check.save
format.html { redirect_to #rad_check, notice: 'Rad check was successfully created.' }
format.json { render :show, status: :created, location: #rad_check }
else
format.html { render :new }
format.json { render json: #rad_check.errors, status: :unprocessable_entity }
end
end
end
RadCheck Model
class RadCheck < ApplicationRecord
has_one :rad_user_group, dependent: :destroy
after_initialize :add_rad_user_group
before_save :set_radcheck
def add_rad_user_group
self.rad_user_group ||= RadUserGroup.new if self.new_record?
end
def set_radcheck
self.rad_user_group.username = username
self.op = ":="
self.attribu = "Cleartext-Password"
end
end
class CreateRadChecks < ActiveRecord::Migration[5.2]
def change
create_table :rad_checks do |t|
t.integer :tenant_id
t.string :username
t.string :password
t.string :attribu
t.string :op
t.string :realipaddr
t.string :groupname
t.timestamps
end
end
end
Radgr Model
class Radgr < ApplicationRecord
end
class CreateRadgrs < ActiveRecord::Migration[5.2]
def change
create_table :radgrs do |t|
t.integer :tenant_id
t.string :groupname
t.string :realipaddr
t.boolean :grdefault
end
end
end
RadUserGroup Model
class RadUserGroup < ApplicationRecord
belongs_to :rad_check
end
class CreateRadUserGroups < ActiveRecord::Migration[5.2]
def change
create_table :rad_user_groups do |t|
t.integer :tenant_id
t.string :username
t.string :groupname
t.references :rad_check, foreign_key: true
t.timestamps
end
end
end
Na Model
class Na < ApplicationRecord
end
class CreateNas < ActiveRecord::Migration[5.2]
def change
create_table :nas do |t|
t.integer :tenant_id
t.string :nasname
t.string :realipaddr
t.boolean :active
end
end
end
PhpMyAdmin Query
You should better organize the object associations. The objects obviously having some sort of relations.
Like Radgr model and Na model. I think you can make relation between these two, so later you can use query command all = Radgr.includes(:nas).where("nas.realipaddr =?", "192.168.22.175") or something similar.
Also here is a good site for converting SQL to ActiveRecord query, but you need here to create your models and associations for testing.
Please check:
http://www.scuttle.io/

Instance Variable Definition in Controller Issue: Rails 5

I have a single user table with two roles defined using enums and Single Table Inheritance with two matching user sub classes for Staff and Clinician. Because I need a lot of information about clinicians that I don't need about Staff, I've created a clinician_profiles table and am using after_create :create_clinician_profile in the user model to create a stub for the clinician profile.
I am trying to build a page for clinicians to complete their profiles and am having trouble getting the instance variable defined in the controller so it pulls the appropriate clinician's profile into the view to render the form. I am getting the following error. Note that the user's id I'm logged in as is 104.
ActiveRecord::RecordNotFound in ClinicianProfilesController#edit
Couldn't find ClinicianProfile with 'id'=104
I feel like I'm close but don't know what I'm doing wrong. Any help would be greatly appreciated for a rookie!
I'm landing on the page using this link in the header:
<li><%= link_to "Manage My Profile", edit_clinician_profile_path(current_user) %></li>
Here are relevant parts of my user model:
class User < ApplicationRecord
self.inheritance_column = :role
enum role: { Staff: 0, Clinician: 1}
belongs_to :university
has_many :referral_requests
class Staff < User
validates :university_id, presence: true
end
class Clinician < User
has_many :lists
has_many :universities, through: :lists
has_one :clinician_profile
after_create :create_clinician_profile
end
Here's my ClinicianProfile model:
class ClinicianProfile < ApplicationRecord
has_many :clinician_profile_languages
has_many :languages, through: :clinician_profile_languages
has_many :clinician_profile_races
has_many :races, through: :clinician_profile_races
belongs_to :clinician
end
clinician_profiles schema
create_table "clinician_profiles", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "firstname"
t.string "lastname"
t.string "address1"
t.string "address2"
t.string "city"
t.string "state"
t.string "zip"
t.boolean "accepting_patients"
t.integer "rate"
t.string "license_number"
t.string "license_state"
t.string "school"
t.integer "year_graduated"
t.string "accepts_insurance"
t.boolean "sliding_scale"
t.text "bio"
t.boolean "verified"
t.integer "years_licensed"
t.integer "years_of_experience"
t.integer "clinician_id"
end
ClinicianProfilesController
class ClinicianProfilesController < ApplicationController
def edit
#clinician_profile = ClinicianProfile.find(params[:id])
end
def index
end
def show
end
def create
end
end
Here's the beginning of my view (edit.html.erb in /views/clinician_profiles)
<%= form_for(#clinician_profile) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
def edit
#clinician_profile = ClinicianProfile.find(params[:id])
redirect_to :index and return unless #clinician_profile
end

Save active record object without reference id

I have the following migrations:
class CreateMothers < ActiveRecord::Migration[5.0]
def change
create_table :mothers do |t|
t.string :name
t.timestamps
end
end
end
and:
class CreateSons < ActiveRecord::Migration[5.0]
def change
create_table :sons do |t|
t.string :name
t.references :mother
t.timestamps
end
end
end
Whenever I try to save a Son object with the mother_id field blank, I get the error: "Mother must exist"
Is there a way to save this without the mother_id field?
In your Son model, just add the optional param to make it work:
class Son < ApplicationRecord
belongs_to :mother, optional: true
end
In default, rails set it to be true, so let use false instead, the detail was described here

ActiveRecord: find appropriate translation

I have models:
Category:
class Category < ActiveRecord::Base
has_many :entities, as: :resourcable
end
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.text :short_descr
t.text :full_descr
t.timestamps
end
end
end
Language:
class Language < ActiveRecord::Base
has_many :entities, as: :resourcable, dependent: :destroy
validates :code, uniqueness: true
end
class CreateLanguages < ActiveRecord::Migration
def change
create_table :languages do |t|
t.string :name
t.string :code
t.timestamps
end
end
end
And Entity:
class Entity < ActiveRecord::Base
belongs_to :language
belongs_to :resourcable, polymorphic: true
end
class CreateEntities < ActiveRecord::Migration
def change
create_table :entities do |t|
t.integer :language_id
t.string :name
t.text :short_descr
t.text :full_descr
t.references :resourcable, polymorphic: true
t.timestamps
end
end
end
In Categories there are default values for fields (short_descr, full_descr), In Entities there are translations for this fields. I need to render as json all Categories with appropriate translations: at first, I need to take Language with appropriate code (for example ru), next, I need to find all language Entities for this language, next, if Entity have filled short_descr and full_descr I need to render Category with this values, else I need to render the Category with default values (this values in Categories table). How to do this? I prefer ActiveRecord buy consider pure SQL.
EDITED
Now I'm trying to use gem 'squeel':
Language.joins{entities.category}.
select{coalesce(entities.short_descr, categories.short_descr)}.
where{languages.code == 'en'}
but it doesn't work (undefined methodshort_descr' for nil:NilClass`). There is the problem?
Entity.joins(:language, :category).
select('categories.*, coalesce(entities.short_descr, categories.short_descr) as short_descr,
coalesce(entities.full_descr, categories.full_descr) as full_descr').
where('languages.code = ?', 'en')

Rails 2: joint multi table

Sorry, i'am newbie
I have database:
Migrate
-Mst_group tble
class CreateMstGroups < ActiveRecord::Migration
def self.up
create_table :mst_groups do |t|
t.string :group_name
end
end
end
-Mst_japan
class CreateMstJapans < ActiveRecord::Migration
def self.up
create_table :mst_japans do |t|
t.string :name_level
end
end
end
-Tbl_user
class CreateTblUsers < ActiveRecord::Migration
def self.up
create_table :tbl_users do |t|
t.string :login_name, :null =>false,:limit =>15
t.string :password,:null =>false,:limit =>50
t.string :full_name,:null =>false
t.string :full_name_kana
t.string :email,:null =>false
t.string :tel,:null =>false,:limit =>15
t.date :birthday,:null =>false
t.references :mst_groups
end
end
end
-Tbl_detail_user_japan
class CreateTblDetailUserJapans < ActiveRecord::Migration
def self.up
create_table :tbl_detail_user_japans do |t|
t.date :start_date
t.date :end_date
t.integer :total
t.references :tbl_users
t.references :mst_japans
end
end
end
Model
class MstGroup < ActiveRecord::Base
has_many :tbl_users
end
class MstJapan < ActiveRecord::Base
has_many :tbl_detail_user_japans
end
class TblUser < ActiveRecord::Base
belongs_to :mst_group
has_one :tbl_detail_user_japan
end
class TblDetailUserJapan < ActiveRecord::Base
belongs_to :tbl_user
belongs_to :mst_japan
end
Controller
def index
#user= ???????
end
How to write command select : login_name, full_name, full_name_kana, email, tel, group_name, name_lever, start_date, end_date, total in controller
It depends on how you want to retrieve the User object. You need to tell Rails how to find the TblUser object. If, for example, the user ID is known, let's say in a variable called 'id' then you would do:
def index
#user=TblUser.find(id)
end
It depends on your application logic how Rails would know which user you need. You may need an input from the user in case of log in, etc.
(Typically in Rails you would call the table 'Users', by convention tables and classes have the same name and then you wouldn't need to call the class TblUser)
That is all you need in the controller, you don't need to tell it which fields you want.
Then in the View you can access all the fields:
Fields on TblUser, example:
<%= #user.email %>
You can access the fields from related objects through the relations, example:
<%= #user.mst_group.group_name %>
Hope that helps to get you started.

Resources