Retrieve information from associated model - ruby-on-rails

I have 3 models:
class Brand
attr_accessible :obs, :site, :title
has_many :equipments
end
class Equipment
attr_accessible :brand_id, :category_id, :lending_id
belongs_to :brand
has_many :lendings
end
class Lending
attr_accessible :equipment_id
belongs_to :equipment
end
I'm trying to show the brand of an associated equipament:
Brand: <%= #lending.equipment.brand %>
that command show this: Brand:0xab7f2c8
As you can see, there's no association between brand and lending models and for me its strange if i do that. I want to use the equipment/brand association to retrieve the :title information and show it on my lending view.
Can anyone help me?

You can either use a delegate in Lending:
delegate :brand, :to => :equipment, allow_nil: true
Or you can setup a has-one-through association in Lending:
has_one :branch, :through => :equipment
Either way, you can now call branch directly from a Lending instance, and work on it (almost) as if it were a regular association.

Use delegate
class Lending
attr_accessible :equipment_id
belongs_to :equipment
delegate :brand, :to => :equipment, :allow_nil => true
end
now you can use
<%= #lending.brand.title%>

Related

ActiveAdmin: Add association on object creation

When creating a new object and connecting it with existing (has_many :through) resources I need to:
Save the new object first
Edit this newly created object again to add connections to the nested resources.
Cumbersome! It seems ActiveAdmin tries to create the association first, then the main object. Is it somehow possible to do this object creation + associating nested resources in one go?
Is case a more concrete example is needed, here is an example of my data model and ActiveAdmin setup:
Person < ActiveRecord::Base
has_many :organizations, through: :person_organizations
end
Organization < ActiveRecord::Base
has_many :people, through: :person_organizations
end
PersonOrganization < ActiveRecord::Base
belongs_to :person
belongs_to :organization
validates :person, presence: true
validates :organization, presence: true
end
form do |f|
f.inputs do
f.input :name
end
f.inputs 'Organizations' do
f.has_many :person_organizations, do |connection_f|
connection_f.input :organization, as: :select,
collection: Organization.select[:id, :name],
member_label: proc { |org| org.name }
end
end
end
You have to add
accepts_nested_attributes_for :organizations, allow_destroy: true
and if you haven't also the
has_many :person_organizations
in your Person model, and you can place
f.input :organizations, :multiple => true
in your form. Also make sure you permit the correct params in your activadmin register block. In this case it would be
permit_params :name, :organization_ids => []
Read carefully: https://activeadmin.info/5-forms.html#nested-resources and https://activeadmin.info/2-resource-customization.html#setting-up-strong-parameters
I like to decorate multiple select inputs with the select2 javascript library. Let me know if something does not work out.

validate uniqueness of field in scope

I have a user model which has a polymorphic relationship to teachers, students, and admin. These three types of users each belong to a school. I would like to have it so the username of the user is unique within a school. How would I write the validations to accomplish this?
Here is what my models look like:
class User < ActiveRecord::Base
belongs_to :profileable, :polymorphic => true
delegate :school, :to => :profileable
end
class Student < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end
class Teacher < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end
class Admin < ActiveRecord::Base
belongs_to :school
has_one :user, :as => :profileable
delegate :name, :username, :to => :user
end
I'm quite sure you will need to use a custom validator for this. The delegated attribute will not be usable in the User model. What you could do is also include the school_id in the User method and set it using before_validate every time. Then you'd be able to use the "simple" uniqueness validator:
validates :username, :uniqueness => {:scope => :school_id}
However, a custom validator joining the school_id of the profileable parent would probably be a cleaner way to go.

Rails: ActiveRecord association won't work when specifying foreign key

I am trying to set up an ActiveRecord association. Below are my two classes and I'm specifying the foreign_key option that I've read about here: api.rubyonrails.foreignkey
School
class School < ActiveRecord::Base
has_many :students, :foreign_key => :institutionid
attr_accessible :name, :city, :state, :zipcode, :institutionid
end
Student
class Student < ActiveRecord::Base
belongs_to :school, :foreign_key => :institutionid
attr_accessible :firstname, :lastname, :institutionid
end
Database Schema
dbo.school {id:int, institutionid:int, name:nvarchar(255), city:nvarchar(255), state:nvarchar(2), zipcode:nvarchar(25),}
dbo.student {id:int, institutionid:int, firstname:nvarchar(255), lastname:nvarchar(255)}
When trying to access the above with <% debug #school.students %> I get the following output on the page --- []
Can anyone help me find out what I'm doing wrong? Or provide some other ways of troubleshooting the issue? Thanks!
I think you are asking for all of the students that have an institutionid of 433. However institutionid is not the primary key of School (and I wonder why not?). So when you say school.students, the query will use the value in 'id' not in 'institutionid'. I would try changing Student's association to:
belongs_to :school, :foreign_key => :institutionid, :primary_key => :institutionid
and change School to:
has_many :students, :foreign_key => :institutionid, :primary_key => :institutionid

How to use foreign key in model and form builder?

I have two models: User and Location as below:
class User < ActiveRecord::Base
attr_accessible :location, :password, :user_name, :password_confirmation
validates :location, :user_name, :presence => true
validates :password, :presence => true, :confirmation => true
has_one :location, :foreign_key => 'location'
end
class Location < ActiveRecord::Base
attr_accessible :loc_id, :loc_name
belongs_to :user, :foreign_key => 'loc_id'
end
You can see that I use the custom foreign_key for the models. I use form builder to build a user sign up form, but when I submit data the error occurs:
Location(#2170327880) expected, got String
I use simple_form to build the form, related code is:
= f.input :location, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}
How can I resolve this problem? Or must I use the default foreign_key like location_id for the association?
Thanks.
Update:
When I rename the location field in User model to loc_id and remove the :foreign_key like this:
class User < ActiveRecord::Base
attr_accessible :loc_id, :password, :user_name, :password_confirmation
validates :loc_id, :user_name, :presence => true
validates :password, :presence => true, :confirmation => true
has_one :location, :foreign_key => 'location'
end
class Location < ActiveRecord::Base
attr_accessible :loc_id, :loc_name
belongs_to :user
end
It works fine. But I still want to know how to associate the User and Location model.
P.S. I use Location model to store the country code and country name, which will never update by User.
It sounds like you actually want to have
class User < ActiveRecord::Base
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :users
end
This means that a user has a location_id column. If you do things the other way around (user_id column on location) then a given location can only be associated to one user. The rails way is that location_id on users 'points' at the id column in the locations table. If you want it to point at a different column, use the :primary_key option (The :foreign_key option would be if you wanted the column on users to be called something other than location_id)
In terms of the form, you can't do f.select :location - forms don't know how to transfer a complicated object like that. In these cases you want to set the form to control the location_id attribute, i.e.
= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.id]}
If you go down the route of having the location id column refer to the loc_id column on location, then you'd need to change that to be
= f.input :location_id, :collection => Location.all.collect {|c| [c.loc_name, c.loc_id]}
Personally if you're only just starting out with rails I'd stick to the defaults
It looks like you're misusing foreign key. In the User model, you should have just has_one :location and the location model should have a user_id attribute. In the location model, you only need to write belongs_to :user. A foreign key is always an index into another (foreign) table.
class User < ActiveRecord::Base
# stuff
has_one :location
end
class Location < ActiveRecord::Base
# more stuff
belongs_to :user
end
If what you ultimately want to do is select all users with the same location, you might want to set the models up a little differently. Right now, because each user has its own location record in the location table, you would end up with duplicate locations, and you'd have to find all of these for a unique location and extract the user_ids from them.
Instead do this
class User < ActiveRecord::Base
has_one :placement
has_one :location, through: :placement
end
class Placement < ActiveRecord::Base
belongs_to :user
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :placements
has_many :users, through: :placements
end
. As for the migrations, Placement should have a :user_id and :location_id. You can drop the :user_id that you currently have in Location. What this code says is that we have many users and we have many unique locations, and we place users in unique locations by creating placements, which indicate that a user with :user_id is located in location with :location_id. Also, don't forget to add a the line
add_index :placements, [:user_id, :location_id], unique: true
so that you can't place a user in a location more than once.
EDIT: Forgot to add: you can get all users in a location by simply getting the location record and calling location.users

Adding items to cart with different product types

Hi developers. I am having problem adding items to cart. From rails agile book. If I want to add to products which has different attributes (Apartments, Cars)
class Products
has_many :line_items
attributes :name, :price, :content
end
class LineItem
belongs_to :products
belongs_to :carts
end
class Cart
has_many :line_items
end
class Car
attributes :name, :car_type, :color
end
class Apartment
attributes :name, :size, :location
end
class Order
attr :buyer_details, :pay_type
end
Customer adds products to the cart and e.x. 2 bedroom for rent, and limo to rent and wants to pay. How to add to cart. If I put apartment_id and car_id to the lineitems, will it polluted? Please I need correct approach, right practice. Thanks for all.
Look up polymorphic associations if you definitely want to keep it all in the LineItems. Then make LineItems a poly for products, apartment and car. But I think you're design is really bad here. Buying and renting are very different. With renting you will have a duration, a single address or registration which must not be double booked. Go back and work on your ERD.
One option of better design:
NB i've changed LineItem to be CartItem for clarity.
class Products
has_many :cart_items
has_many :order_items
attributes :name, :price, :content
end
class Cart
has_many :line_items
end
class CartItem
belongs_to :products
belongs_to :carts
end
class CartRental
# :cart_rentable_type, :cart_rentable_id would be the fields for the polymorphic part
belongs_to :cart_rentable, :polymorphic => true
belongs_to :carts
attributes :from, :till
end
class Order
attr :buyer_details, :pay_type
end
class OrderItem
belongs_to :products
belongs_to :order
end
class Rental
belongs_to :rentable, :polymorphic => true
belongs_to :order
# :rentable_type, :rentable_id would be the fields for the polymorphic part
attributes :from, :till, :status
end
class Car
attributes :name, :car_type, :color
has_many :cart_rentals, :as => :cart_rentable
has_many :rentals, :as => :rentable
end
class Apartment
attributes :name, :size, :location
has_many :cart_rentals, :as => :cart_rentable
has_many :rentals, :as => :rentable
end

Resources