Rails two to many relationship - ruby-on-rails

I have a problem that I solved in a certain way that is not satisfying.
I have a Game model and in a game there are always two teams involved that are part of Team model. I reference to these teams by ids team1_id and team2_id. From my views whenever I want to pull the entire Team record, I have to do a find every time.
I was wondering if there is any way to reference these two teams without going through a many to many relationship or is it the only way? It would be almost a 2-Many relationship, I know that doesn't exist but I would like to know the best way to solve this kind of problems.
Thank you,
This is a snapshot of my migrations:
create_table :games do |t|
t.datetime "time"
t.integer "team1_id"
t.integer "team2_id"
create_table :teams do |t|
t.references :city
t.references :user
t.string "name", :default => "", :null => false

The way you have it setup is the right way.
On the Game model make two team references, team1 and team2
class Game
belongs_to :team1, class_name: 'Team'
belongs_to :team2, class_name: 'Team'
end
Then you can just call team1 an team2 on the game instance and it will pull the teams for you.
game = Game.first
game.team1
game.team2
Or you can drop the team1 and team2 id's from the Game model and create a join table with games and teams, an you'd just call "game.teams"

Related

How to keep attributes from two different tables synchronized in RoR?

What I want is to be able to easily be able to find the team name associated with a membership without having to send another request to the database. My plan was to just add a team_name attribute to the Memberships table, but I can't think of a way to have this new attribute stay in sync with the name attribute in the Teams table. In other words, I want it to be the case that, if the owner of a team changes the team's name, then all memberships to that team will be updated (with the new team name) as well.
Here is what my setup looks like. I've fairly new to Rails.
/app/models/membership.rb
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
end
/app/models/team.rb
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
end
/app/db/schema.rb
ActiveRecord::Schema.define(version: 20161022002620) do
create_table "memberships", force: :cascade do |t|
t.integer "user_id"
t.integer "team_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "teams", force: :cascade do |t|
t.string "name"
t.integer "user_id"
end
end
If there is a better way to achieve what I am asking, then please let me know as well.
With this relational data your membership doesn't need a team name attribute - it is already available through the team association.
Generally there's no reason to keep data 'in sync' in this way unless you're performing some sort of computation. You don't need to store a name attribute on Membership - you can just use the existing one in Team.
I have seen people add duplicate database columns because they don't know how to traverse through associations. But unless you're using some noSql system, this isn't the 'right way' to do it - there is an underlying SQL API (through ActiveRecord) that performs lookups very efficiently.
in response to your comment. Do this:
class Membership < ActiveRecord::Base
def name
team.name
end
end

Associating multiple existing records

I'm creating an application where one user becomes the account_manager of an account. What I want to do is to add other users to the account. A user can only have one account but an account can have many users.
class Account < ActiveRecord::Base
has_many :users
belongs_to :account_manager, :class_name => 'User', :foreign_key => 'account_manager_id'
end
class User < ActiveRecord::Base
has_one :account
What I'm totally stuck on is having a place where the account manager can either select the user from a dropdown, type in their name, or use some other type of selection. If I try to do this in console each new user I add replaces the last instead of adding to it. here is my schema for accounts:
create_table "accounts", force: :cascade do |t|
t.integer "user_id"
t.integer "account_manager_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I've tried using collection_select but I think that is only for :though associations. I'm also thinking I probably need a join table but I don't know how to set it up. The thing that is tripping me up most is that I won't be creating new objects, I only want to add existing users to existing accounts. I'm just looking for someone who can talk through this with me.
In your User model:
class User < ActiveRecord::Base
belongs_to :account
end
While in your question, you are writing has_one. You can't write has_many in one model, and has_one in other model. There needs to be belongs_to in one model.
Edit:
The model that belongs_to, always saves the foreign keys in its table. So users would save account_id in it. In order to get all the users of an account, you would simply do:
Account.first.users # As an account `has_many` users

Relations not working as expected

This will be fairly quick and easy for most of you...I have a table called types, and another called projects. A project can only have one type, but a type can have many projects. For instance a community garden project and a playground project can both have the type of 'greenspace'. So I have set up a has_many association. In my types model I have this:
has_many :projects
and in my projects model I don't have anything (I previously had has_one in it but upon looking at the docs it seemed incorrect). In the projects#show view I would like the name of the type to display. The parks project's view should say 'greenspace'. but I am getting the error
undefined method `type' for #<Project:0x007ffdd14fcde8>
I am trying to access that name using:
<h3>Type: <%= #project.type.project_type %> </h3>
i have also tried:
<h3>Type: <%= #project.type_id.project_type %> </h3>
but of course type_id gives a number, and there is no project_type for a number. project_type being the name of the column which holds the string data 'greenspace'. Am I accessing it wrong? Or have I set it up incorrectly?
Also in my schema, projects looks like this:
create_table "projects", force: :cascade do |t|
t.string "type_id"
t.text "description"
t.integer "money_needed"
t.integer "money_raised"
t.float "interest_offered"
t.datetime "end_date"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.text "url"
end
Project can belong_to both. Like this
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :type
belongs_to :user
#...
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
#...
end
#app/models/type.rb
class Type < ActiveRecord::Base
has_many :projects
#...
end
In the Project model you should state:
belongs_to => :type
In general, for most associations there is going to be an inverse. Not always, as you might have multiple associations in Type for Project. For example as well as your current has_many :projects, you might have others to return only projects that are unfinished, and such an association would not need an inverse.
Bear in mind that when you state: #project.type Rails is going to look for a method on #project. The association is what provides this method, and effectively the result is then the Type object that is referenced by the Project. It's important to realise that #project.type only returns a Type because the association tells it to -- the magic does not extent to just inferring that that is what is wanted.

Model style for Ruby on Rails application

So currently my application has a model called Vendors. I want each Vendor to have one or many owners, one or many members, and then people who track or follow the Vendor. I'm trying to decide the best layout for this and I've messed around with a few things. All of the Owners, members, and followers would also be coming from my User model. Therefore I'm thinking of making one association table with booleans as to whether the User is a follower, owner, or member.
For clarification - Owner and member would be people who work at the vendor
schema.db
create_table "vendor_relationships", force: true do |t|
t.integer "user_id"
t.integer "vendor_id"
t.boolean "owner"
t.boolean "member"
t.boolean "follower"
t.datetime "created_at"
t.datetime "updated_at"
end
but I'm wondering if this is the best way to go about this when it comes to speed and/or quality of code. Previously I had created separate tables for vendor_owners, vendor_members, and vendor_followers. Then I associated them with a has_many :through relationship, but I'm stuck onto which way is the best.
They would all have separate logic, or rather separate permissions as to what they were allowed to view and edit with respect to the vendor. I looking at cancan and pundit for role based authorization, but I didn't think it really applied for this situation.
A role based authorization could work if I build the schema like this
create_table "vendor_relationships", force: true do |t|
t.integer "user_id"
t.integer "vendor_id"
t.string "role"
t.datetime "created_at"
t.datetime "updated_at"
end
And then check for possible user roles against the string but I'm not sure which of these is the better option.
EDIT 1:
I'm going with the t.string "role" route, but I'm wondering how I can write the code into my model so that the associations with role = "owner" would be accessible by doing Vendor.owners... etc with Vendor.members and Vendor.followers.
This is what my code looks like currently.
has_many :owners, through: :vendor_relationships, class_name: "User", -> { where role: owner }
This for each variation - owners, members, followers. I've also tried using
has_many :owners, through: :vendor_relationships, class_name: "User", conditions: => ['vendor_relationship.role = "owner"']
but everything is giving me syntax errors here. Would appreciate some help thanks.
I think you are on the right track. I would do the following:
Model your owners, members, and followers all as User, but then give them each a role. Start with a single role per user to keep it simple. See: https://github.com/ryanb/cancan/wiki/Role-Based-Authorization
In the many-to-many joins table, I would remove the booleans and instead let your association handle that logic.
has_many :owners, through: :vendor_members, class_name: "User", conditions: {"users.role = 'owner'"}
Then you can just interact with owners/members/followers like so: owners = vendor.owners
I would also consider changing the name of the vendor_members table since one of the types is also members. Maybe something like vendor_relationships

Table Relationship Reference Ruby on Rails

I am currently working on a web application where a contractor (electrician, roofer, plumber) etc can make a proposal online. Pictures, youtube videos of the project, and a text description will be provided to the contractor from the customer.
So far I am working on the pictures feature using carrierwave
This is the table of this model in my schema
create_table "project_pictures", force: true do |t|
t.string "name"
t.string "picture"
t.datetime "created_at"
t.datetime "updated_at"
end
Here are my two records in my rails console
ProjectPicture Load (0.4ms) SELECT "project_pictures".* FROM "project_pictures"
=> #<ActiveRecord::Relation [#<ProjectPicture id: 2, name: "Request for Siding Quote Customer A", picture: "siding.jpg", created_at: "2013-08-15 16:10:22", updated_at: "2013-08-15 16:47:02">, #<ProjectPicture id: 1, name: "Request for Siding Quote Customer A", picture: "sidingrequest.jpg", created_at: "2013-08-14 01:54:27", updated_at: "2013-08-15 16:47:39">]>
The thing is I am trying to link multiple pictures to one customer. Lets say the above two pictures belong to one customer and there are two rows because there are two pictures.
How do I reference that in the table, lets say I have one customer and thats me "judy"
both the record should reference judy's id?
and then eventually in the view, I can draw both pictures out using an image tag that belong to the customer id 1 - with name = "judy" or just customer id = 1?
If I am not making things clear please let me know, I am not that familiar with tables relationships and which relationship will help me the most.
create table :projects do |t|
t.references :clients
t.timestamps
end
create_table :pictures do |t|
t.string :name, null:false
t.string :location, null:false
t.references :projects, null:false
t.timestamps
end
... meanwhile.. back in the model layer
class Project < ActiveRecord::Model
has_many :pictures, :dependent => destroy
end
class Picture < ActiveRecord::Model
belongs_to :project
validates_presence_of :project
end
To get all Judy's pictures
Client.where("name like ?", "Judy").projects.first.pictures
I would have set it up like this
user has_many project_pictures
project_pictures belongs_to user
your project picture table should have a row for the user_id.

Resources