So in my Rails web application I have a section of code that prompts underage users to input a parent/guardian email when creating an account. In my database I would like to store this as the corresponding ID but naturally the user will want to enter an email.
this part of my form looks like this:
<%= f.label :guardian_email, "Guardian Email" %>
<%= f.text_field :guardian_email, class: 'form-control' %>
The corresponding code in my model looks like this:
def guardian_email
User.find_by_id(guardian_id).email if guardian_id
end
def guardian_email=(g_email)
guardian = User.find_by_email(g_email)
print guardian.id
self.guardian_id = guardian.id
print self.guardian_id
end
(the print lines I've added while debugging) However, submitting the form does not update the desired attribute. I then tried from the console and typed:
User.find_by_email("example-5@site.org").guardian_email=("example-7@site.org")
I get the output:
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example-5@site.org"], ["LIMIT", 1]]
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? LIMIT ? [["email", "example-7@site.org"], ["LIMIT", 1]]
88 => "example-7@mtb.org"
but when I load the user afterwards the attribute guardian_id is still nil. what is odd is that both of the print statements return the proper value (in this case 8) but the assignment has never occured, despite the fact the second 8 that is printed out is the value "self.guardian_id".
I've followed railscasts #16 - virtual attributes to arrive at the code I have now, Thanks.
EDIT:
The reason the above code would not function is because I was permitting the actual attribute in the controller as opposed to the virtual one. Although as max pointed out several other bad practices are also present in this code.
Something I still do not understand though is that calling the guardian_email=() method in the console still does not update the attribute, but it works fine on the site.
Thats a very convoluted approach to a simple task.
class User < ApplicationRecord
belongs_to :guardian,
class_name: 'User',
optional: true
def guardian_email=(email)
self.guardian ||= User.find_by_email(email)
@guardian_email = email
end
def guardian_email
@guardian_email || guardian.try(:email)
end
end
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :email
# ...
t.belongs_to :guardian, foreign_key: { to_table: :users }
t.timestamps
end
add_index :users, :email, unique: true
end
end
You'll also want a custom validation which kicks in if guardian_email
is not blank which checks if the email actually belongs to a user.
class User < ApplicationRecord
# ...
validate :guardian_exists, unless: ->{ self.guardian_email.blank? }
# ...
private
def guardian_exists
errors.add(:guardian_email) unless guardian || User.find_by_email(guardian_email)
end
end