how to write this query of many-to-many base in rails - ruby-on-rails

Hey guys
I'm new to rails, There's a lot of eye-opener for me, and I write some code and it seems no efficient, I paste my code below, could you help me find a better way to write this.
videos table:
class CreateVideos < ActiveRecord::Migration
def self.up
create_table :videos do |t|
t.string :title
t.string :desc
t.string :tudou
t.string :otherurl
t.timestamps
end
end
def self.down
drop_table :videos
end
end
drummers table:
class CreateDrummers < ActiveRecord::Migration
def self.up
create_table :drummers do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.string :nick_name
t.boolean :gender
t.timestamps
end
end
def self.down
drop_table :drummers
end
end
and I set them to simple many-to-many association
class CreateDrummersVideosJoin < ActiveRecord::Migration
def self.up
create_table :drummers_videos, :id => false do |t|
t.integer "drummer_id"
t.integer "video_id"
end
end
def self.down
drop_table :drummers_videos
end
end
I want to find all the title of drummer first name is "Jojo" last name is "Mayer"'s video
my code:
title = Drummer.where(:first_name => "Jojo", :last_name => "Mayer").first.videos.each {|t| t.title}
This return all the column's data, not the only the title I want
and since the there's only one result return named "Jojo Mayer", But the return value is activeRelation, I can't call videos, so my work around is using :first to get the video instance in order to call the videos. I know it's definitely not the way doing it
any suggestion?

You need a join table in between them like you infer. But that join table in Rails does not have to be created in a migration. It can be done exclusively in the models.
#drummer.rb
belongs_to :drummer_videos, :polymorphic => true
has_many :videos, :as => :drummer_videos
#video.rb
belongs_to :drummer_videos, :polymorphic => true
has_many :drummers, :as => :drummer_videos
Make sure that the drummer TABLE and video TABLE have a drummer_videos_id attribute.
Then you can call your Drummer..
Drummer.where(:first_name => "Jojo", :last_name => "Mayer").videos.each {|t| t.title}

First off, if you want to get only the title attribute from the videos you should use map or collect instead of each, so something like this:
Drummer.where(...).first.videos.map{ |t| t.title }
or even shorter:
.map(&:title)
Second, it seems to me that if you really want to get all the videos from a single Drummer object, than using first in some way or another, like you do, is a pretty good option.
Otherwise, if you want to get all videos from different Drummers according to a certain criteria, then you should probably call Video directly and then join or include the Drummer. Perhaps like this:
Video.joins(:drummers).where("drummers.first_name = 'jojo' AND drummers.last_name = 'Mayer'").map(&:title)

Related

Rails/SQL no results show from Query

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.

Nearly identical methods in PagesController produce an undefined method error in Rails 4

I have three tables in my Rails 4 app -- one for Game, Category, and Topic. Both Category and Topic have a column for :name, while Game includes information like starts_at for when a game begins.
In my PagesController, I can show data from both Game and Topic by using find_by with the params value:
topic = Topic.find_by_name(params[:topic])
#games = Game.for_topic(topic).upcoming.order(:starts_at)
This works fine.
What's weird is that when I use the same reasoning but with Category instead of Topic, like so:
category = Category.find_by_name(params[:category])
#games = Game.for_category(category).upcoming.order(:starts_at)
I receive an error message:
undefined method `for_category'
This is confusing to me since I am definitely defining category and the using it in my for_ expression. Am I making an error in my thinking?
Additional
CreateCategories Migration
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.belongs_to :topic, index: true
t.string :name, :null => false
t.timestamps
end
end
end
CreateTopics Migration
class CreateTopics < ActiveRecord::Migration
def change
create_table :topics do |t|
t.string :name, :null => false
t.timestamps
end
end
end
I think you setup the named scope for_topic in the Game model. But is missing the for_category, which is why it is failing.
Try setting the named scope for_category in Game model.

Querying the database passing multiple parameters rails

I have a user table and an activity table. The user has many activities. This is what i have in my users table:
class SorceryCore < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :surname
t.integer :previous_award
t.integer :chosen_award
t.string :email, :null => false
t.string :crypted_password
t.string :salt
t.timestamps
end
add_index :users, :email, unique: true
end
This is what I have in my activities table:
class CreateActivities < ActiveRecord::Migration
def change
create_table :activities do |t|
t.integer :activity_type
t.string :activity_name
t.string :approver_email
t.references :users, index: true, foreign_key: true
t.timestamps null: false
end
end
In my view I want to show the activity_name, where user_id = the current user's id, and where the the activity_type = 1. I'm not sure where to write this method or how to call it either. I have read through the following link but can't seem to get anything working. http://guides.rubyonrails.org/active_record_querying.html
I think I need to use something along the lines of this:
Activity.where("activity_type <= ?", 1).where("user_id <= ?", current_user.id)
But I'm not sure if this is supposed to go into a method in the controller or the model, and I'm not sure which controller or model it's supposed to go into
In the User model:
# user.rb
def activity_by_type(type = 1)
self.activities.where(activity_type: type).first
end
and then, you can call current_user.activity_by_type(<specify the type here>)
You can use the above method to get any of the activity type for the specified user, by specifying the type in the method call.
One advice I'll give though is to try and use the concept of enums to categorize your activity_type column in the activities table. An example on how, can be found here.
You simply have to query on the current_user.activities association:
#activities = current_user.activites.where(activity_type: "1")
You could also use a scope (which is what SunnyK recommended):
#app/models/user.rb
class User < ActiveRecord::Base
has_many :activities
scope :by_type, ->(type = 1) { activities.where(activity_type: type) }
end
--
If you only wanted to return a single record, you'd have to replace .where with .find_by:
#activity = current_user.activities.find_by activity_type: 1
--
Since you're using an enum, you may wish to use the built-in class method that you'd be able to call:
enum activity_type: [:sports, :photography]
#activity = current_user.activities.sports

is their any date filter in ruby?

1)
i have a leave request form in my HRM application, i want that when a user enter the "from_date", the "to_date" should be greater than "from_that"?
2)
In my app i have a leave model in which user can defined leave type and day's allowed.In my second model leave_request user can request for leave according to leave type.Everything is working well.Now i want to display leave balance available to user on basis of allowed day's in leave_request form?
Migration leave.rb:
class CreateLeaves < ActiveRecord::Migration
def self.up
create_table :leaves do |t|
t.integer :user_id
t.integer :company_id
t.string :leave_type
t.integer :allowed_leaves
t.text :description, :limit => 500
t.timestamps
end
end
def self.down
drop_table :leaves
end
end
And leave_request.rb:
class CreateLeaveRequests < ActiveRecord::Migration
def self.up
create_table :leave_requests do |t|
t.integer :employee_id
t.integer :leave_type
t.date :from_date
t.date :to_date
t.text :reason_for_leave
t.string :contact_during_leave, :limit => 10
t.integer :user_id
t.integer :company_id
t.timestamps
end
end
def self.down
drop_table :leave_requests
end
end
And can i validate number of leaves available like if user do not have enough leave balance than their should be a message like"You do not have enough leave to request " etc.
Your first question is not very comprehensible. What is "from_that"? If you're asking how to check if one date is before or after another, it's easy: Date and DateTime include Comparable so you can compare any two just like you would two numbers, e.g.:
>> DateTime.now > 1.day.ago
# => true
>> DateTime.now > Date.tomorrow
# => false
You can write your own validator, something like this (untested code):
class LeaveRequest < ActiveRecord::Base
validate :has_enough_leave
def has_enough_leave
if( to_date - from_date > employee.allowed_leave.days )
errors.add(:to_date, "You do not have enough leave to requestt")
end
end
...
I have done my first problem in following way:
class LeaveRequest < ActiveRecord::Base
belongs_to :user
belongs_to :leave
validate :from_date_and_to_date
def from_date_and_to_date
if(to_date != from_date)
errors.add(:to_date, " should be greater than from date" )
end
end
Hi friends here i came with a new answer of my second question
i used following line of code to fix my problem:
<label>Leave Type</label>
<label>Balance</label>
<% for leave in #leave_types %>
<%= leave.leave_type %>
<%= leave.allowed_leaves%>
<% end %>
This displayed all my leave_types in my new leave_request form.

Rails 3 single table inheritence w/ has_many question

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.

Resources