Versions: CENTOS7, mysql2('>= 0.3.13', '< 0.5'), rails('4.2.6')
index.html.erb
<% #sections.each do |section| %>
<tr>
<td><%= section.course_id %></td>
<td><%= section.term_id %></td>
<td><%= section.user_id %></td>
</tr>
<% end %>
sections controller
class SectionsController < ApplicationController
before_action :authenticate_account!, except: [:show]
def index
#sections = User.find_by_account_id(current_user).courses
end
def show
end
end
createSections migration
class CreateSections < ActiveRecord::Migration
def change
create_table :courses do |t|
t.integer :course_id
t.timestamps null: false
end
create_table :terms do |t|
t.integer :term_id
t.timestamps null: false
end
create_table :users do |t|
t.integer :user_id
t.timestamps null: false
end
create_table :sections do |t|
t.belongs_to :course, index: true
t.belongs_to :term, index: true
t.belongs_to :user, index: true
t.timestamps null: false
end
end
end
course.rb model
belongs_to :user
has_many :sections
has_many :terms, :through => :sections
term.rb model
belongs_to :user
has_many :sections
has_many :courses, :through => :sections
section.rb model
belongs_to :course
belongs_to :term
belongs_to :user
user.rb
has_many :sections
has_many :courses, :through => :sections
has_many :terms, :through => :sections
Expected result: List the current(logged in) user's courses/terms/ID
Current result: blank
This is my first time working with rails and SO, I tried changing the relationships a few times to see if anything would change but not sure how to approach this. I have tried using ActiveRecord:Associations as a reference. What do I need to do to make this work?
If you already have current_user then you can just call the related models directly.
#sections = User.find_by_account_id(current_user).courses
to
#sections = current_user.courses
The first thing to address is that you typically want separate migrations for each distinct thing that you're migrating. It's a convention that helps you keep fine-grained control over your changes, and it helps keep your migrations clean.
Sometimes, though, you actually want to have a "mass" migration. You have a mass-migration here. In the mass-migration circumstance, the migration name should reflect the combined purpose, so you'd want to name it something like CreateCoreTables; CreateSections is too narrow a name for creating multiple tables. You will also need to change the name of the migration file to be 2016XXXXXXXX_create_core_tables.rb, where XXXXXXXXX is left as the previous value.
Next, you'll want to correct your use of keys (the _id columns), as these are improperly declaring the association fields, which will cause the associations to not work (or work incorrectly).
Instead, you want something like this:
class CreateCoreTables < ActiveRecord::Migration
def change
create_table :courses do |t|
t.timestamps
end
create_table :terms do |t|
t.timestamps
end
create_table :users do |t|
t.timestamps
end
create_table :sections do |t|
t.integer :user_id, null: false
t.integer :term_id, null: false
t.integer :course_id, null: false
t.timestamps
end
end
end
It's difficult to tell what the actual intended relationships are from the code provided. It would be worth reading on Active Record Migrations to make sure that you understand what relationships you intend, and how to describe them. While you're working out the migrations, keep revisiting the model relationships, as well. These are the bedrock of the application, so you want to spend time getting them right.
Remember, for ActiveRecord relationships, these rules will guide you:
If a model owns another model, use has_many or has_one
In the owned model, use belongs_to
If a model needs access to another model (but doesn't own it), use a has_many, :through relationship
Models cannot have has_many or has_one relationships directly to each other; you need an intermediate table in that case with the requisite belongs_to for each of the other tables
Once you have the migrations and relationships worked out, you can move on to the query. You can use Rails Eager Loading to optimize the query to retrieve the associations at the same time. This will address your current functional needs, and prevent an N+1 query issue at the same time.
#sections = Section.joins(courses: :terms).where(user: current_user)
When you have retrieved #sections, you can do these types of actions to get the data that you want. The courses and terms members are collections, and you can interact with them as though they were arrays:
#sections.each do |section|
puts "Section: #{section.id}"
puts "Number of user sections: #{#sections.courses.length}"
section.courses.each do |course|
puts "Course: #{course.id}"
end
puts "Number of user terms: #{#sections.terms.length}"
#sections.terms do |term|
puts "Term: #{term.id}"
end
puts "User's email: #{#sections.user.email}"
end
Once you've mastered these, you've got the basics of Rails. Work on one model/controller at a time to keep from overcomplicating the work; you can always add on more once once component is working like you expect. Always make sure that you have your foundation working before you move onto new aspects of the app that will depend on it.
Also, remember to use the Rails guides. They're very helpful, so keep them on hand at all times while you're learning. SO is also a great resource, and make sure that you ask pointed questions, so that you can get direct answers.
Related
Ok so what I have been doing for categories is I have had 2 tables Categories(id, name) and SubCategories(id, name, category_id) that are related through the models. I set inverse_of as you will see below.
My question is when I use to code in PHP years ago we use to have one table "Categories" and it had id, name, parent_id(0 by default) we then used that one table to control the outputs of navigations, breadcrumbs, and other navigational elements.
class CreateCategories < ActiveRecord::Migration[5.1]
def change
create_table :categories do |t|
t.string :name
t.timestamps
end
end
end
class CreateSubCategories < ActiveRecord::Migration[5.1]
def change
create_table :sub_categories do |t|
t.string :name
t.integer :category_id
t.timestamps
end
end
end
class Category < ApplicationRecord
has_many :sub_categories, inverse_of: :category
end
class SubCategory < ApplicationRecord
belongs_to :categories, inverse_of: :sub_category
end
I have been programming in Ruby on Rails now for over 4 years and I have yet to find a real nice "Rubyway" to achieve this. Over the years I have seen examples of using what I am already using with the 2 table method, however this does not seem very intuitive because when the system gets many categories and sub categories like 100's the page load time will be impacted in processing. Is anyone useing or know of a one table method like
class CreateCategories < ActiveRecord::Migration[5.1] def change
create_table :categories do |t|
t.string :name
t.integer :parent_id
t.timestamps
end
end
end
The problem I have always had is the model and how to get the system to realize that a record can belong to a record on the same table. I have been able to achieve it manually but I have not found a way to set it up where formtastic and other gems like rails admin would play nice with it.
Seems that you're looking for a tree structure. acts_as_tree has been around for some time. If you're using PostgreSQL, the ltree extension may be of interest as well (along with the pg_ltree gem).
I'm building a tech-specific pricegrabber-like web app, and I have a model that carries params that are common in all products. This model is called Product. Then I have one model for each type of product that I'm going to work with, for example, I'm now trying to build the first specific model, which is Videocard. So, the Product model always must have one Specific model, in this case Product-Videocard.
At this moment I'm stuck finding a way to make a product and a specific model always come tied together whenever I reach to them, be it in an index view, show view, form_for, a search, etc. But I can't picture in my head how a form will create an item and its specifications and insert a foreign key into another model with only one submit request.
Below are both models and the migrations for each:
class Product < ApplicationRecord
#belongs_to :productable, :polymorphic => true
has_one :videocard, dependent: :destroy
# Comment for this Stackoverflow question: the way I'm thinking I
# should have to make tons of has_one associations, for the other
# products. Is there a DRY way to do this?
has_many :prices, through: :stores
validates :platform, presence: { message: "should be specified." }
validates :name, presence: { message: "should be specified." }
validates_associated :videocard
end
class Videocard < ApplicationRecord
belongs_to :product
end
Migrations (shortened to make this question as clear as possible):
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.references :productable, polymorphic: true, index: true
t.string :name
t.string :image
t.string :partnum
t.string :manufacturer
t.string :platform #mobile, desktop, server, laptop
t.timestamps
end
end
end
class CreateVideocards < ActiveRecord::Migration[5.0]
def change
create_table :videocards do |t|
t.references :product, index: true
t.integer :memory
t.string :interface
# [...lots of attributes...]
t.integer :displayport
t.integer :minidisplayport
t.integer :tdp
t.timestamps
end
end
end
Also how can I make it so that Product only needs one has_one association, instead of using multiple ones. Remember that Videocard will have one type of specification, Memory will have other, and so on.
I've looked for many solutions on the web and I can't seem to find my answer.
I have a polymorphic association for a table links that it linked to many other tables.
Here is my models a bit simplified:
links.rb
class Links < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
events.rb
class Event < ActiveRecord::Base
has_many :links, as: :linkable
accepts_nested_attributes_for :links
end
here is the admin form
events.rb
ActiveAdmin.register Event do
form do |f|
f.has_many :links do |link_f|
link_f.inputs "links" do
link_f.input :url
end
end
f.actions
end
end
Here's what in my schema.rb
create_table "links", force: true do |t|
t.string "url"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "linkable_id"
t.string "linkable_type"
end
It throws me that error: uninitialized constant Event::Link
I can't seem to find the problem and it is driving me nuts...
It seems like a relation is missing or something but I can't find it.
Thanks a lot for every one that can help!
I think the problem is in the way you named your models. Models are always declared as singular entities, not plural.
You should:
Rename links.rb to link.rb
Rename events.rb to event.rb
Rename class Links < ActiveRecord::Base to class Link < ActiveRecord::Base
and see if that helps.
I have the following data models and would like to render a json hash that includes information from each model. For example, client.id, client.name_first, client, name_last, every workout description for each client and each exercise description for each workout.
class Client < ActiveRecord::Base
belongs_to :account
belongs_to :trainer
has_many :programs
has_many :workouts, :through => :programs
end
class Workout < ActiveRecord::Base
has_many :programs
has_many :clients, :through => :programs
has_many :routines
has_many :exercises, :through => :routines
end
class Exercise < ActiveRecord::Base
has_many :routines
has_many :workouts, :through => :routines
end
My database migrations:
class CreateClients < ActiveRecord::Migration
def change
create_table :clients do |t|
t.integer :account_id
t.integer :trainer_id
t.string :name_first
t.string :name_last
t.string :phone
t.timestamps
end
end
end
class CreateWorkouts < ActiveRecord::Migration
def change
create_table :workouts do |t|
t.string :title
t.string :description
t.integer :trainer_id
t.timestamps
end
end
end
class CreateExercises < ActiveRecord::Migration
def change
create_table :exercises do |t|
t.string :title
t.string :description
t.string :media
t.timestamps
end
end
end
I am able to return the workouts for a particular client:
#client = Client.find(params[:id])
clients_workouts = #client.workouts.select('workouts.*,programs.client_id').group_by(&:client_id)
render json: clients_workouts
And I am able to return the exercises for a particular workout:
#workout = Workout.find(params[:id])
exercises_workouts = #workout.exercises.select('exercises.*, routines.workout_id').group_by(&:workout_id)
render json: exercises_workouts
However, I do not know how to return the data with information from all three tables (Client, Workout, Exercise) included (joined through Programs and Routines). Is this possible? And how is it done?
First, I'm not really sure what's happening in your query:
clients_workouts = #client.workouts.select('workouts.*,programs.client_id').group_by(&:client_id)
Is this not sufficient?
#client.workouts
Now, on to the answer... assuming I'm still following:
ActiveRecord offers a .to_json method, which is what's being implicitly called here. The explicit version would be e.g.
render json: clients_workouts.to_json
Knowing that, you can look up to_json in the api (here's some good documentation even though it shows as deprecated: http://apidock.com/rails/ActiveRecord/Serialization/to_json). But, basically, the answer is to start with the root object -- the client I believe -- and build the included objects and attributes/methods from there in the options hash.
render json: #client.to_json(include: { workouts: { include: :exercises } })
You can customize which attributes or methods are included from each related model if needed, just dig into the documentation a little. Have fun!
Very possible and their are different ways to optain this.
One, without any 3rd party library is to use includes, just as if you were solving an n+1 problem or…
Use a much cooler approach and use active model serializers
Active Model Serializers
I've been trying to setup a Single Table Inheritance model in Rails 3 in which the parent class also contains a has_many relationship. Unfortunately I can't get it to work. Here are three classes as an example:
class Article < ActiveRecord::Base
has_many :paragraphs, :dependent => :destroy, :autosave => true
end
class Paragraph < ActiveRecord::Base
belongs_to :article
end
class SportsArticle < Article
end
And here's the migration that would be used to set this up:
class AddTables < ActiveRecord::Migration
def self.up
create_table :articles do |t|
t.string :type, :null => false # for STI
t.string :title, :null => false
t.timestamps
end
create_table :paragraphs do |t|
t.references :article, :null => false
t.timestamps
end
end
def self.down
drop_table :articles
drop_table :paragraphs
end
end
When I set it up this way and I try to create a new SportsArticle, say by doing the following:
SportsArticle.create(:title => "Go Giants")
I always get the following error:
"TypeError: can't convert String into Integer"
I have no idea how to fix this issue and have tried finding a solution online to no avail. Does anybody who has experience with STI models see anything wrong? Here's the link to the documentation on the create method if it will help in diagnosing the problem:
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-c-create
Try renaming :type to something else, like :article_type
eg:
t.string :article_type, :null => false # for STI
The error was being caused due to a naming collision. I was using a name for one of my models called "attributes" which was causing the problem. The hint that eventually diagnosed the problem came from the Rails Association Documentation.