I'm writing a basic CRM app to learn Ruby on Rails 4.2, and I have an odd pair of associations (or maybe not so odd). If this were straight SQL and another platform it would be straightforward, but I'm not sure if I'm doing it properly in ROR.
The business requirement is that each company has one or more users. Simple enough. (Relevant portions of the model below.)
user.rb
belongs_to :company
company.rb
has_many :users
An additional requirement is that each company can have one primary user. I need to be able to reference the user's info (name, email, etc) within a Companies index and show view. This is where I'm kind of uncertain. Should the models look like this (it doesn't make sense to me, but I can't think of another way)?:
user.rb
belongs_to :company
company.rb
has_many :users
has_one :user
Here's an abbreviated schema. The user_id in companies is the FK to store the "primary contact":
create_table "companies", force: :cascade do |t|
t.string "companyname"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "phone"
t.integer "company_id"
end
Thank you for your help.
user.rb
belongs_to :company
company.rb
has_many :users
**belongs_to :primary_user, class_name: "User"**
in company you should have primary_user_id column. You can also define has_one on user like this:
user.rb
belongs_to :company
**has_one :primary_company, class_name: "Company", foreign_key: "primary_user_id"**
company.rb
has_many :users
belongs_to :primary_user, class_name: "User"
user.rb
belongs_to :company
company.rb
has_many :users
Above models are proper. I think for the primary user of a company you can have one more model like primary_user_membership.rb
belongs_to :company
belongs_to :user
create_table "primary_user_memberships", force: :cascade do |t|
t.integer "company_id"
t.integer "user_id"
end
Based on your info, your model is correct.
user.rb
belongs_to :company
company.rb
has_many :users
You can do Company.first.users to see all the first company's users.
Let's say User 1 is the Primary user of Company 1, in rails console, you can check by doing Company.first.users.first
You can also check User 1's info by doing :-
user1 = Company.first.users.first
user1.username
When you generate the User model, you can do: rails g model User username email phone company:belongs_to
Your schema model should look like:-
create_table "companies", force: :cascade do |t|
t.string "companyname"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "phone"
t.integer "company_id"
end
Related
Question how can i filter my get api function using the ID ni my URI pattern
for example if the client side uses the localhost:3000/users/1/projects
he will get the projects with a field of "user_id" equal to the URI pattern /users/:id
#routes
resources :users do
resourcers :projects
end
#project controller
def index
#projects = Project.all
render json: #projects
end
#users assocciation
has_many :projects, dependent: :destroy
has_many :tasks, dependent: :destroy
has_many :comments, dependent: :destroy
#project association
class Project < ActiveRecord::Base
has_many :task, dependent: :destroy
belongs_to :user
end
#DB schema
create_table "projects", force: :cascade do |t|
t.string "name"
t.text "descriptions"
t.date "startDate"
t.date "dueDate"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.boolean "project_confirmation"
end
what we want is when the client side uses this route /users/:user_id/projects(.:format) he accepts projects with field of user_id similar to the routes /users/:id
Use AMS or Jbuilder for that.
https://github.com/rails-api/active_model_serializers
https://github.com/rails/jbuilder
I have a person model that i want to connect to multiple addresses (main_address, work_address etc.)
In app/models/person.rb:
class Person < ActiveRecord::Base
belongs_to :main_address, class_name: :Address, foreign_key: :main_address_id
belongs_to :work_address, class_name: :Address, foreign_key: :work_address_id
belongs_to :invoice_address, class_name: :Address, foreign_key: :invoice_address_id
belongs_to :further_address, class_name: :Address, foreign_key: :further_address_id
end
And an address model:
class Address < ActiveRecord::Base
belongs_to :person
end
address migration:
class CreateAddresses < ActiveRecord::Migration
def change
create_table :addresses do |t|
t.string :street
t.string :zip
t.string :city
t.string :country
t.string :lockbox
t.string :type
t.references :person, index: true, foreign_key: true
t.timestamps null: false
end
end
end
app/models/main_address.rb:
class MainAddress < Address
end
When i try to save an address in rails console (the person with id 1 was already created):
2.2.1 (main):0 > main_address = Address.create(type: "main_address", street: "Schumannstr.15", zip: "D-53113", city: "Bonn", country: "Germany", person_id: 1)
I get the error:
ActiveRecord::SubclassNotFound: Invalid single-table inheritance type: main_address is not a subclass of Address
What am i doing wrong?
I think there may be a problem with your belongs_to associations. It seems to me that an address belong to a person, and a person has many addresses. If conceptually this does not represent your requirements then the rest of this answer will need to be adapted.
Also, in the original example you are specifying foreign keys that don't exist as columns in the addresses table. Each address will just be referred to through its id and type pair.
I would expect this type of code to work:
# app/models/person.rb
class Person < ActiveRecord::Base
has_many :addresses
end
# app/models/address.rb
class Address < ActiveRecord::Base
belongs_to :person
end
# app/models/main_address.rb
class MainAddress < Address
end
The type column in your table should trigger STI, allowing you to do something like:
person = Person.create(...)
person.addresses.create(street: "Schumannstr.15", zip: "D-53113", city: "Bonn", country: "Germany")
Hopefully this will work for you. If not, feel free to edit or comment as needed.
I have several models in a separate folder called jira (instance.rb, generic_field.rb, etc.). They are are all namespaced under JIRA, for example JIRA::Instance < ActiveRecord::Base, JIRA::GenericField < ActiveRecord::Base. Here are the two models:
class JIRA::GenericField < ActiveRecord::Base
self.table_name = "jira_generic_fields"
belongs_to :jira_instance, class_name: JIRA::Instance
end
class JIRA::Instance < ActiveRecord::Base
self.table_name = "jira_instances"
has_many :jira_generic_fields, dependent: :destroy, class_name: JIRA::GenericField
end
The DB schema for the tables:
create_table "jira_generic_fields", force: true do |t|
t.string "jira_id"
t.string "name"
t.integer "jira_instance_id", null: false
end
create_table "jira_instances", force: true do |t|
t.string "jira_link"
t.string "crowd_link"
end
In my rails console i create a JIRA::Instance object and when I try to destroy it I get this:
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'jira_generic_fields.instance_id' in 'where clause': SELECT `jira_generic_fields`.* FROM `jira_generic_fields` WHERE `jira_generic_fields`.`instance_id` = 1
Why does ActiveRecord use jira_generic_fields.instance_id instead of jira_generic_fields.jira_instance_id and how can I fix this while keeping the models under the same JIRA namespace?
In the end specifying the foreign_key in the models solved this ...
class JIRA::GenericField < ActiveRecord::Base
self.table_name = "jira_generic_fields"
belongs_to :jira_instance, foreign_key: 'jira_instance_id', class_name: JIRA::Instance
end
class JIRA::Instance < ActiveRecord::Base
self.table_name = "jira_instances"
has_many :jira_generic_fields, dependent: :destroy, foreign_key: 'jira_instance_id', class_name: JIRA::GenericField
end
I don't really like it but it will have to do for now.
I have the following associations:
class Campaign < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :campaign
belongs_to :venue
accepts_nested_attributes_for :venue
end
class Venue < ActiveRecord::Base
has_many :events
end
And I have the following form:
<%= form_for([#campaign, #event]) do |f| %>
<%= f.fields_for :venue do |v| %>
<%= v.text_field :search %>
<% end %>
<% end %>
Anything in the fields_for block is not showing up. But the funny thing is if i change f.fields_for :venue to f.fields_for :venues, the :search field shows up.
But :venues shouldn't be plural on :events. Even in rails console Event.venues does not work
NoMethodError: undefined method `venues'
It also generates the wrong field names
This makes me think I have my model associations jacked up, but after reviewing the docs. it does not seem so. Here is my schema for reference:
create_table "campaigns", force: true do |t|
t.integer "user_id"
t.string "title"
t.text "description"
t.integer "image_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "events", force: true do |t|
t.integer "campaign_id"
t.datetime "start_date"
t.datetime "end_date"
t.integer "venue_id"
t.integer "image_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "title"
t.text "description"
end
create_table "venues", force: true do |t|
t.float "latitude"
t.float "longitude"
t.string "address1"
t.string "address2"
t.string "city"
t.string "zip"
t.string "state"
t.string "country"
t.string "name"
t.string "gid"
t.datetime "created_at"
t.datetime "updated_at"
end
See the similar SO Posts
1.Does accepts_nested_attributes_for work with belongs_to?
2.Getting fields_for and accepts_nested_attributes_for to work with a belongs_to relationship
As mentioned in one of those posts,one way of achieving it is changing the accepts_nested_attributes_for to the has_many side
class Campaign < ActiveRecord::Base
has_many :events
accepts_nested_attributes_for :events
end
class Event < ActiveRecord::Base
belongs_to :campaign
belongs_to :venue
end
class Venue < ActiveRecord::Base
has_many :events
accepts_nested_attributes_for :events
end
If it is not,you might want to build a venue for events with #event.build_venue in the controller before calling fields_for.
Hope it helps!
right now im building a form that accepts a many to many using a select multiple. after creating the form, im trying to display the information collected in the SHOW page however i dont know how to display that data in embedded ruby. join model is CardTypesList
Models
class Card < ActiveRecord::Base
self.inheritance_column = nil
validates :name, presence: true, uniqueness: {case_sensitive: false}
has_many :card_type_lists
has_many :card_types, through: :card_type_lists
accepts_nested_attributes_for :card_type_lists
end
class CardType < ActiveRecord::Base
has_many :card_type_lists
has_many :cards, through: :card_type_lists
end
class CardTypeList < ActiveRecord::Base
belongs_to :cards
belongs_to :card_types
accepts_nested_attributes_for :card_type
end
NEW form using select
<%= f.label :types %>
<%= f.select :card_type_ids, CardTypes.all.collect{|x| [x.name, x.name]}, {},{:title => "Select a Type", :multiple => true, :class => 'selList'} %>
Embedded ruby trials
<td class="card-td"><%= #card.card_types %></td>
Expected: ["Type 1", "Type2"]
this renders the page, but yields (in text):
<ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_CardTypes:0x00000102f58a18>
EDIT: schema added.
ActiveRecord::Schema.define(version: 20140120042152) do
create_table "card_type_lists", force: true do |t|
t.integer "card_type_id"
t.integer "card_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "card_types", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "cards", force: true do |t|
t.string "name"
t.string "set"
t.string "card_types"
t.string "colors"
t.string "cost"
t.string "rarity"
t.string "oracle"
t.float "value"
t.integer "number_owned"
t.string "notes"
t.string "img_link"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "types_mask"
end
add_index "cards", ["name"], name: "index_cards_on_name", unique: true
end
and this is my controller:
class CardsController < ApplicationController
def new
#card = Card.new
#card.card_type_lists.build.build_card_type
end
def show
#card = Card.find(params[:id])
end
def create
#card = Card.new(card_params)
if #card.save
redirect_to #card
else
render 'new'
end
end
private
def card_params
params.require(:card).permit(:name, :set, {:card_types => []}, :color, :cost, :rarity,:oracle,:value, :number_owned,:notes)
end
end
Should be:
class CardTypeList < ActiveRecord::Base
belongs_to :card
belongs_to :card_type #Singular
end
Some more things to think about:
Does your join model have the correct columns?
You should use accepts_nested_attributes_for
Look at how you're calling the data
Schema
Join models in has_many :through have to have foreign_key references to both models they're joining. The way you do this is to use a schema like this:
card_types_lists
id | card_id | card_type_id | other | information | created_at | updated_at
When you mentioned the error no such column: card_type_lists.card_types_id, it generally means you either don't have the correct column in the db, or your reference is incorrect. Looking at it, it's your association (referencing plural instead of singular)(fixed above)
Forms
Something you should consider is using accepts_nested_attributes_for to send the correct data to the nested models
This is when you want to create some records in either model, and works by allowing you to define "new" objects for your other models in your parent model, passing the data to your child models, like this:
#app/models/card.rb
Class Card < ActiveRecord::Base
has_many :card_type_lists
has_many :card_types, through: :card_type_lists
accepts_nested_attributes_for :card_type_lists
end
#app/models/card_type_list.rb
Class CardTypeList < ActiveRecord::Base
belongs_to :card
belongs_to :card_type
accepts_nested_attributes_for :card_type
end
#app/controllers/cards_controller.rb
def new
#card = Card.new
#card.card_types_lists.build.build_card_type
end
Data
If you want to show your associative data correctly, you should try this:
#app/views/cards/show.html.erb
<%= #card.card_types %>