Mysql2::Error: Unknown column - mysql

I work on rails 3.2.18 and mysql 5.6.20. I created migration which create new table and It looks like this:
class CreateRequest < ActiveRecord::Migration
def change
create_table :requests, id: false do |t|
t.string :id, null: false
t.integer :lesson_id, null: false
t.integer :user_id, null: false
t.boolean :approved, null: false, default: false
end
add_index :requests, :id, unique: true
end
end
I decided that I will use uuid as primary key. New record in DB I create like following:
def create_request(lesson)
request = Request.new.tap do |req|
req.id = SecureRandom.uuid
req.lesson_id = lesson.id
req.user_id = #user.id
end
request.save
end
So we have Request model and It is related to two other tables Lesson (one-to-one) ans User (one-to-many). No I will show you all models.
class PublicizeRequest < ActiveRecord::Base
self.table_name = "requests"
self.primary_key = "id"
has_one :lesson
belongs_to :user
end
class Lesson < ActiveRecord::Base
(...)
belongs_to :publicize_request
end
class User < ActiveRecord::Base
(...)
has_many :publicize_requests
end
Now I can describe main problem. If I get request from DB and I want to get user data I can do this simple:
#request.user
But If I want get lesson in the same way I get following error:
Mysql2::Error: Unknown column 'lessons.request_id' in 'where clause': SELECT `lessons`.* FROM `lessons` WHERE `lessons`.`archived` = 0 AND `lessons`.`request_id` = 'd0da41df-a9be-4575-ada0-538abe1f54a3' LIMIT 1
I am newbie in rails and I don't know how to deal with this. Thanks for all answers.

Your migration and relationship definition are not matching. The rule is that whichever table you have foreign key in, you define belongs_to relationship on that table's model.
You have defined lesson_id column in requests table, which means that Request model should have belongs_to :lesson.
class PublicizeRequest < ActiveRecord::Base
...
belongs_to :lesson
...
end
The reason you are getting the error is because you have has_one :lesson in Request model which would imply that you have belongs_to :request in your Lesson model if you have defined it, but you do not have request_id column in your lessons table.

Related

Rails: query for 2-step relation through join-table + has many relation

I am working on improving my database scheme. Currently I use a has_many through structure with a land model/table a shippingservice model/table and a postzone model/table, which connects land and shipping tables.
I can query this way:
#shippingservices = #cart.available_shipping_services.includes(:lands, :postzones).where('postzones.land_id = ?', params[:id])
And get all shipping services for a given land (country).
But this way I end up with a lot of repetition in the postzone table and maintenance is quite difficult. It would be much easier if I could collect lands in zones with many lands and than connect shipping services to a zone. Just like in this scheme:
How would I query in this case for all shipping services for a given land?
For a join table I would do something like:
#shippingservices = #cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
Which does not work, since I get:
Mysql::Error: Unknown column 'lands_zones_join.zone_id' in 'where clause': SELECT `shippingservices`.* FROM `shippingservices`
INNER JOIN `zones` ON `zones`.`id` = `shippingservices`.`zone_id`
INNER JOIN `lands_zones` ON `lands_zones`.`zone_id` = `zones`.`id`
INNER JOIN `lands` ON `lands`.`id` = `lands_zones`.`land_id`
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
WHERE (weightmin <= 50 AND weightmax >= 50 AND height >= 3 AND shippingservices.shippingcarrier = '1') AND (length >= 210 AND width >= 149) AND (lands_zones_join.zone_id = '2')
Is this even possible? How can I get this to work?
Thank you in advance!
UPDATE
models/shippingservices.rb
class Shippingservice < ActiveRecord::Base
belongs_to :zone
has_many :lands, :through => :zone
end
app/models/zone.rb
class Zone < ActiveRecord::Base
has_and_belongs_to_many :lands, :join_table => "lands_zones"
has_many :shippingservices
end
app/models/landszone.rb
class LandsZone < ActiveRecord::Base
belongs_to :land
belongs_to :zone
end
app/models/land.rb
class Land < ActiveRecord::Base
has_and_belongs_to_many :zones, :join_table => "lands_zones"
has_many :shippingservices, :through => :zones
end
db/schema.rb
create_table "lands", :force => true do |t|
t.string "name"
end
create_table "lands_zones", :force => true do |t|
t.integer "land_id"
t.integer "zone_id"
end
add_index "lands_zones", ["land_id"], :name => "index_lands_zones_on_land_id"
add_index "lands_zones", ["zone_id"], :name => "index_lands_zones_on_zone_id"
create_table "shippingservices", :force => true do |t|
t.integer "zone_id"
t.string "name"
t.string "shippingcarrier"
end
create_table "zones", :force => true do |t|
t.string "name"
end
I reduced it by deleting timestamps, etc. but kept all necessary parts.
#shippingservices = #cart.available_shipping_services.joins(:lands => :zones).where('lands_zones_join.land_id = ?', params[:id])
The lands_zones_join table in the where clause is incorrect. Rails is seeing this as a derived table name and applying two extra joins seen here:
INNER JOIN `lands_zones` `zones_lands_join` ON `zones_lands_join`.`land_id` = `lands`.`id`
INNER JOIN `zones` `zones_lands` ON `zones_lands`.`id` = `zones_lands_join`.`zone_id`
This is leading to the duplication in the query results.
The relations from Shippingservice to Land has been set up correctly in the models and schema. We can therefore join the tables directly and query the lands table itself for the id:
#cart.available_shipping_services.joins(:lands).where(lands: {id: params[:id]})

Search In a column which stores an array

In my table professional_infos table there is one column primary_skill_ids which stores an array of skills for particular user. I want to list all the user who has some particular skill sets.
example -
user1 has primary_skill as ["1","3","5","9","4"]
user2 has primary_skill as ["1","7","9","4"]
user3 has primary_skill as ["1","4","11"]
user3 has primary_skill as ["7","9","4"]
user4 has primary_skill as ["1","7","9"]
user5 has primary_skill as ["7","9"]
now I want to perform a search like get me all the users which have any or all of the skill primary_skill_ids as ["1","4]
please help me to write a rails query to do so.
I have done like the following
wildcard_search = "%#{params[:search_title]}%"
# key skills and best in search
#key_skills=[]
#key_skills.each do | sk |
# here I am thinking of looping through the ids and do a where clause on column primary_skill_ids but dont know its good idea
end
Serializing relation data in a string column violates the very idea of a relational database - which is that you have foreign key columns that point to other tables. Using array* or string types in the DB for associations is a really bad idea:
It is highly inefficient to search strings compared to an indexed column containing integers or uiids.
There is no referential integrity enforced by foreign key constraints.
Its not how ActiveRecord works - which means you'll be wasting time by fighting the framework.
Instead you want to create a many-to-many association through a join table:
class User < ApplicationRecord
has_many :user_skills
has_many :skills, through: :user_skills
end
class Skill < ApplicationRecord
has_many :user_skills
has_many :users, through: :user_skills
end
class UserSkill < ApplicationRecord
belongs_to :user
belongs_to :skill
end
In this example we are using a table named user_skills to join the two models:
create_table "user_skills", force: :cascade do |t|
t.integer "user_id"
t.integer "skill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["skill_id"], name: "index_user_skills_on_skill_id", using: :btree
t.index ["user_id"], name: "index_user_skills_on_user_id", using: :btree
end
You can then setup UI controls by using the collection helpers:
# app/views/users/_form.html.erb
<%= form_for(#user) do |f| %>
<%= f.collection_check_boxes :skill_ids, Skill.all, :id, :name %>
<% end %>
# app/controllers/users_controller.rb
class UsersController < ApplicationController
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to #user
else
render :new
end
end
# PUT|PATCH /users/:id
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render :edit
end
end
def user_params
params.require(:user)
.permit(skill_ids: [])
end
end
PostgreSQL supports standard SQL arrays and the standard any operation syntax:
So, you can use an SQL like
where name ilike any (array['%val1%', '%val2%'])
Using Rails it can be written like:
User.where('primary_skill ilike any (array[?])', ["1","4"] )
Additional info:
In your situation you can use "ILIKE", or "LIKE", but the difference is:
"ILIKE" is case-insensitive
"LIKE" is case-sensitive.
Usage of wildcards (% from '%val1%') are detailed here

Rails Unique Constraints Model + Database Level and Race Conditions

I currently have the following migration for my UserGroups has_many :through association:
class CreateUserGroups < ActiveRecord::Migration
def change
create_table :user_groups do |t|
t.integer :user_id, :null => false
t.integer :group_id, :null => false
t.timestamps null: false
end
end
end
With the following model and unique constraint:
class UserGroup < ActiveRecord::Base
belongs_to :user
belongs_to :group
validates_uniqueness_of :user, :scope => :group, :message => :not_unique
end
The uniqueness validation is working great, but what if competing threads in my application cause a race condition and it makes past the application-level check? How can I write a migration to protect against a duplicate row in my join table for users and groups at the database level?
You need to add an unique index
add_index :user_groups, [:user_id, :group_id], unique: true

How do tables join in Rails 4?

I have these models:
class A < ActiveRecord::Base
has_and_belongs_to_many :Bs
end
class B < ActiveRecord::Base
has_and_belongs_to_many :As
end
class CreateAs < ActiveRecord::Migration
def change
create_table :as do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreateBs < ActiveRecord::Migration
def change
create_table :bs do |t|
t.string :name
t.timestamps null: false
end
end
end
If table 'as' has the following entries:
"A1"
"A2"
"A3"
and table 'bs' has the following entries:
"B1"
"B2"
"B3"
Does table 'as' have a foreign_key of b and vice-versa?
If yes, then how does internal mapping take place in Rails 4? How it will map? And how can I join and display both these tables?
The table does not have the foreign keys as it's a has_and_belongs_to_many relationship - they will be linked via a new join table which you need to create:
rails generate migration CreateJoinTableAB a b
You will need to create a join table for that. Something like:
class CreateJoinTableAB < ActiveRecord::Migration
def change
create_join_table :as, :bs do |t|
# add some idexes depending on your use case
# t.index :a_id
# t.index :b_id
end
end
end
Read more about this in the Rails Guide
Look at the Rails Guide for has_and_belongs_to_many. It is all described there.
With a nice picture about the database structure.
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

In rails, how to query fields in join table?

In rails, I have two models, :contest and :problem. Each contest has many problems, and a problem can belong to many contests. A problem in different contests, may has differenet "index".
I use model :contest_problem to join :contest and :problem. In :contest_problem, I have a field index, to mark what's the "index" of problem in contest.
Now, when I execute Contest.find(...).problems.find(...) (... is contest_id or problem_id that I want to query), rails will create a sql query:
SELECT `problems`.*
FROM `problems`
INNER JOIN `contest_problems` ON `problems`.`id` = `contest_problems`.`problem_id`
WHERE `contest_problems`.`contest_id` = 1 AND `problems`.`id` = 1
LIMIT 1
I want to execute this sql query:
SELECT `problems`.*, `contest_problems`.`index`
FROM `problems`
INNER JOIN `contest_problems` ON `problems`.`id` = `contest_problems`.`problem_id`
WHERE `contest_problems`.`contest_id` = 1 AND `problems`.`id` = 1
LIMIT 1
How I should do in rails?
Models:
class Contest < ActiveRecord::Base
has_many :contest_problems
has_many :problems, through: :contest_problems
end
class Problem < ActiveRecord::Base
has_many :contest_problems
has_many :contests, through: :contest_problems
end
class ContestProblem < ActiveRecord::Base
belongs_to :contest
belongs_to :problem
end
Migration:
class CreateContestProblems < ActiveRecord::Migration
def change
create_table :contest_problems do |t|
t.integer :contest_id
t.integer :problem_id
t.string :index, limit: 2
t.timestamps
end
end
end
Try this query:
Problem.includes(:contests, :contest_ problems)
.where(#problem.id)
.where(contests: { id: #contest.id } ).first
This will give you, in one database query, the information that you need.