Is there a way to make a Rails model have another model as an attribute?
E.g. I have a User model with an address attribute, and the address attribute in the User model is its own class.
User Model
----------
lastName:string
firstname:string
address: addressModel
Address Model
-------------
street: string
city:string
zipCode:integer
Yes, you can use associations for that:
class User < ActiveRecord::Base
has_one :address
end
class Address < ActiveRecord::Base
belongs_to :user
end
Note that for this to work you need to have a user_id field in your addresses table.
This will magically give your user object an address attribute of type Address. It also allows you to assign an address to a user. E.g.:
address = Address.find(1)
user = User.find(1)
user.address = address
user.save
user.address.class # => Address
user.address.id # => 1
This is an example of a 1:1 association between user and address. Check the guide I linked to above for explanations about other types of association.
Related
Basically i have user registering himself to the app , by using devise gem.
Instead of having standard sign up form like (email, password) i have an extra 2 fields (name, contact_nr) in total used (name, contact_nr, email, password, password_confirm) fields, :name and :contact_nr attributes exists in 'clients' table only.
Table name: clients
id :integer not null, primary key,
name :string(255)
surname :string(255)
contact_nr :string(255)
user_id :integer
class Client < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
after_create :update_user_client
def name
return unless client
client.name
end
def contact_nr
return unless client
client.contact_nr
end
def update_user_client
Client.last.update_attributes(user: self)
end
end
In my RegistrationsController I have only one method
class RegistrationsController < Devise::RegistrationsController
before_action :create_client
private
def create_client
return if params[:user].blank?
Client
.new(name: params[:user][:name],
contact_nr: params[:user][:contact_nr])
.save(validate: false)
end
end
What bothers me is that kind of writing code, it feels like code smell.
How would you implement it?
Thanks guys looking forward to your answers..
First advice I can give is do not separate client and user into two tables if you don't have valid reasons and/or requirements for now. That would make things much easier.
If you have valid reasons, here are my advices on how to improve your existing state of this code piece:
Rails and all mature gems around it rely on 'convention over configuration', so you should check if there are conventional ways to achieve same results.
In your RegistrationsController instead of doing params[:user].blank? check, you should use Devise's way of doing this, provided with inherited methods as devise_parameter_sanitizer.permit within a before_action callback.
Instead of creating client in your controller, move that to model logic, and in your user model put accepts_nested_attributes_for :client.
Since both of your models(client and user) share same name, put a before_save callback, so that you can pass user's name attribute to client itself.
after_create callback is very risky, since it is not an atomic save (no guarantee that client will be updated after user record is updated.). So don't use it. accepts_nested_attributes_for will handle both create and update calls.
If name attribute for user would be fetched through client only, there is no need to keep name within user.
If you want to access client's contact_nr and name attributes directly from user model, then use delegate method inside it.
Putting all together, I would refactor that code piece as this:
class User < ActiveRecord::Base
has_one :client, dependent: :destroy
accept_nested_attributes_for :client
delegate :name, to: :client
delegate :contact_nr, to: :client
# optional. if you want to keep name attr in both models.
before_save :sync_names
private
def sync_names
self.client.name = name if client.present?
end
end
class RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
protected
def configure_permitted_parameters
added_attrs = [:name, :email, :password, :password_confirmation, client_attributes: [:contact_nr]]
devise_parameter_sanitizer.permit :sign_up, keys: added_attrs
devise_parameter_sanitizer.permit :account_update, keys: added_attrs
end
end
Don't forget to update your signup and account update forms to accept nested attributes for client resource.
As far as you are validating the data with JS and filtering with params.require(:client).permit, the code looks fine. Try to create many differente scenarios in your Rspec. The test usually reveals unexpected flaws.
I have the following tables
User.
Name
Email
..
password_digest
Company.
Name
Address
...
contact_person_id
So my company have a contact_person, which take a user_id, therefor being a foreign key.
I have tried the following.
class Company < ActiveRecord::Base
has_one(:user, foreign_key: 'contact_person_id')
end
class User < ActiveRecord::Base
belongs_to(:company, :class_name => "Company", :foreign_key => 'contact_person_id')
end
But when i try to run my (respond_to) rspecs i get
Failure/Error: before { #company = FactoryGirl.create(:company) }
ActiveModel::MissingAttributeError: can't write unknown attribute `contact_person_id'
My Factory
FactoryGirl.define do
factory :company do
name "Starup Company"
address "Test street 37"
zip 2200
website "http://example.com"
industry "Construction"
contact_person user
end
end
What am i doing wrong? And how would i point to user with a company object?
company.contact_person
2 issues - you have belongs_to and has_one reversed and you are calling contact_person method on a company, but you defined the relationship to be as a user
if you want to use the contact_person method on the company, try this:
class Company < ActiveRecord::Base
belongs_to(:contact_person, class_name: 'User')
end
class User < ActiveRecord::Base
has_one(:company, :foreign_key => 'contact_person_id')
end
This page has a good overview: http://guides.rubyonrails.org/association_basics.html#choosing-between-belongs-to-and-has-one The foreign key ALWAYS is found in the table with the belongs_to association.
The way you wrote it each user can only have one company that they are the contact person for.
To write the rails code the way you have it, you'd have to have the foreign key in the users table.
I have three models, Account, User and Contact:
class User < ActiveRecord::Base
has_one :account
has_many :contacts, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :account
end
I'm trying to scope build a new contact through the user record, like this in my contacts controller.
def create
#contact = current_user.contacts.build(params[:contact])
respond_to do |format|
if #contact.save
...
else
...
end
end
end
When I do this, I don't receive any errors, the contact record is saved to the database however the account_id column is not set on the contact, and it is not added to the collection so calling #current_user.contacts returns an empty collection.
Any suggestions?
Using build makes a new instance of Contact in memory, but you would need to manually set the account_id on the record (e.g. #contact.account_id = current_user.account.id), or perhaps set it in a hidden field in the new form used to display the contact for creation such that it is picked up in the params array passed to the build method.
You might also want to consider whether accepts_nested_attributes_for may be helpful in this case. Another option may be to use delegate, although in both cases, your use may be sort of the opposite of what these are intended for (typically defined on the "parent").
Update:
In your case, the build method is added to both the User instance and to the Account (maybe "Owner") instance, because you have both a many-to-many relationship between User and Contact, as well as a one-to-many relationship between Account and Contact. So to get the account_id I think you would need to call Account's build, like
#contact = current_user.accounts.contacts.build(params[:contact])
Does this work?
I have 3 models: house, tenant, and plumber. tenant belongs_to house and house has_one tenant. I want to use a custom EachValidator that requires data from plumber. To accomplish this, in my tenant model:
attr_accessor :plumber_limit
In the controller, I have set the plumber_limit with:
house.tenant.plumber_limit = plumber.value
When the tenant validation fires, the tenant.plumber_limit value is nil and, consequently, fails. I've tried adding a public definition for plumber_limit. I've tried adding attr_accessible :plumber_limit. I've tried different notations. Is this failing because tenant is being used in a child context? If so, how do I get the plumber_limit into the tenant's validation?
By request, here's the validation code:
class UniquePlumberAssignment < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.property.limit(record.plumber_limit).each |p|
...
end
end
end
I am working with a Rails polymorphic inheritance configuration - I have the following setup:
User < ActiveRecord::Base
belongs_to :rolable, :polymorphic => true
Student < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
Teacher < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
I want to be able to capture email address for teachers and a username for students (who won't typically have an email address). I defined those as attributes of the User model, but now I'm stuck when I try to do validations for Student and Teacher. I didn't define them in their respective models because I'm using Devise and there will be other user types. Abstracting what is currently type to a Role pattern isn't a good fit for my particular scenario either.
Since username and email are properties of User what I basically want to do is check if the rolable_type field from the polymorphic relationship is type student and if so, make username required and email not, but in the new method that property isn't set. However Rails 'knows' this is a Student, so it feels like there's some way to check the instance type. The closest link I've found to what I'm shooting for is the third comment to the accepted answer in this question: How to apply different validation rule according to polymorphic association type (Rails)?, but I'm having trouble getting the method_missing syntax correct as I'm not experienced with metaprogramming. Am I on the right track with this? Or is there a simpler way? Or should I move the properties to the polymorphic models instead?