I have a question associated with moving data from one table to another. I am using Postgres as a database.
I have two models:
class User < ActiveRecord::Base
has_many :emails
end
class Email < ActiveRecord::Base
belongs_to :user
end
schema looks like this:
create_table "users", force: :cascade do |t|
t.string "first_name", limit: 255
t.string "last_name", limit: 255
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "emails", force: :cascade do |t|
t.string "email", null: false
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Now I want to move the latest email for the user and save it in users table email column. I can easily do it using Rails models in migration but then when I rename model or remove it. Migration will fail. Is there any easy way to do it with raw sql?
You can use window functions. The example below is a starting point.
UPDATE users u
SET u.email = (SELECT
MAX(FIRST_VALUE(email)) OVER (PARTITION BY user_id ORDER BY created_at DESC)
FROM emails e
WHERE e.user_id = u.id);
You can still use ActiveRecord with fake classes just for the purpose of migration. Something like:
class YourMigration < ActiveRecord::Migration
class FakeUser < ActiveRecord::Base
self.table_name = 'users'
has_many :fake_emails, foreign_key: 'user_id'
end
class FakeEmail < ActiveRecord::Base
self.table_name = 'emails'
belongs_to :fake_user, foreign_key: 'user_id'
end
def change
FakeUser.all.each do |user|
[...]
end
end
end
Related
I'm building three models in my rails application. One model references the same model twice as shown in my DB Schema. The only problem is that when I make a POST Request to create a new record in my shipment table. I get this error:
#<ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: users.sender_id: SELECT \"users\".* FROM \"users\" WHERE \"users\".\"sender_id\" = ? LIMIT ?>
I don't think that I need to add a sender_id & receiver_id column in my users table because the sender_id & receiver_id are basically the User_ID in the users column. Any help would be much appreciated it!
This is my user.rb file:
class User < ApplicationRecord
has_many :shipments
end
This is my shipment.rb
class Shipment < ApplicationRecord
belongs_to :sender, class_name: "User", primary_key: "sender_id"
belongs_to :receiver, class_name: "User", primary_key: "receiver_id"
validates_uniqueness_of :tntcode
end
This is my shipments_controller:
class ShipmentsController < ApplicationController
def index
shipments = Shipment.all
end
def show
shipment = Shipment.find(params[:id])
end
def create
shipment = Shipment.new(shipment_params)
if shipment.save
render json: {status: 'Shipment created successfully'}, status: :created
else
render json: { errors: shipment.errors.full_messages }, status: :bad_request
end
end
def shipment_params
params.require(:shipment).permit(:tntcode, :status, :shipment_type, :weight, :content, :price, :sender_id, :receiver_id)
end
end
And my schema.rb:
ActiveRecord::Schema.define(version: 20180826123320) do
create_table "shipments", force: :cascade do |t|
t.integer "tntcode"
t.string "status"
t.string "shipment_type"
t.integer "weight"
t.string "content"
t.integer "price"
t.integer "sender_id"
t.integer "receiver_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["receiver_id"], name: "index_shipments_on_receiver_id"
t.index ["sender_id"], name: "index_shipments_on_sender_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email", null: false
t.string "role"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "photourl"
t.string "userid"
end
end
You don't want to change the primary_key on your belongs_to associations: that's the other table's ID column (id).
You instead want:
belongs_to :sender, class_name: "User", foreign_key: "sender_id"
belongs_to :receiver, class_name: "User", foreign_key: "receiver_id"
... which is the default, so this should work too:
belongs_to :sender, class_name: "User"
belongs_to :receiver, class_name: "User"
I have a has_many :through association.
#app/user.rb
class User < ApplicationRecord
has_many :members
has_many :projects, :through => :members
end
#app/project.rb
class Project < ApplicationRecord
has_many :members
has_many :users, :through => :members
end
#app/member.rb
class Member < ApplicationRecord
belongs_to :user
belongs_to :project
end
I have the database schema as follows:
create_table "members", force: :cascade do |t|
t.integer "user_id"
t.integer "project_id"
t.integer "is_owner"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["project_id"], name: "index_members_on_project_id"
t.index ["user_id"], name: "index_members_on_user_id"
end
create_table "projects", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
I can get Member ID, User_ID, Project_ID, and is_owner when I use the command #project.members
I can get user_id, first_name, last_name, email, and password when I use the command #project.users
What command should I use to get member_id, first_name, last_name?
I can get what I want using the SQL query SELECT * FROM members INNER JOIN users ON users.id = members.user_id but I don't want to use raw SQL.
Can someone tell me how to convert that query into a Ruby on rails command?
You can get your desired result using following code
Member.joins(:user)
It will generate the same query what you are specifying in your question i.e.
SELECT * FROM members INNER JOIN users ON users.id = members.user_id
I'm sure I'm missing something basic, but I have a model User which belongs_to a Company model which has a name attribute.
How can I sort all users by their Company name?
#users = User.all #sort this collection in order of the associated company's name
class User < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
end
create_table "users", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "company_id"
end
create_table "companies", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
User.includes(:company).order('company.name') . I believe ASC is default, but you can add it if necessary. You may also have to specify the Company table name in the model, or change the table name to 'companies', as that is the default pluralization convention over configuration will assume I believe.
My application uses Ruby on Rails ActiveRecord models, which don't allow for composite keys without installing a third-party gem such as composite-primary-keys. Is there a way I can refactor a composite key into a simple key so it will fit this paradigm, or should I bite the bullet and install the gem?
I'm still at the early design stage so I have no data I need to worry about, and I'd like to stay as true to Rails idioms as possible.
I'm creating a recipe database that can list ingredients and instructions in a step-by-step manner. The database schema is similar to the one shown below, and is using composite keys in the Recipe_Steps and Recipe_Step_Ingredients tables (bottom center of image).
You're very likely overthinking and overcomplicating the problem. Stick with the AR idiom of using a single primary key named id.
For join tables use foreign keys instead of compound PKs. Also stick to the naming conventions unless you want to look inept or annoy other devs by violating the principle of least surprise. That means:
use snake_case for everything (table names, columns, index names etc.)
don't prefix the column with the table name. It just makes every variable in your application longer and is not needed in a ORM.
use _id for foreign key columns. ex; parent_id
use _at for timestamps. ex; confirmed_at
use thing_other_things for join tables unless there is a more descriptive name
Also many of these cases should just use an indirect relation to join up the hierarchy instead of duplicating foreign keys.
This is an example DB schema:
ActiveRecord::Schema.define(version: 20161214013752) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "ingredient_types", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "ingredients", force: :cascade do |t|
t.integer "ingredient_type_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["ingredient_type_id"], name: "index_ingredients_on_ingredient_type_id", using: :btree
end
create_table "recipe_ingredients", force: :cascade do |t|
t.integer "recipe_id"
t.integer "ingredient_id"
t.float "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["ingredient_id"], name: "index_recipe_ingredients_on_ingredient_id", using: :btree
t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id", using: :btree
end
create_table "steps", force: :cascade do |t|
t.integer "recipe_id"
t.integer "ordinal"
t.text "instruction"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["recipe_id"], name: "index_steps_on_recipe_id", using: :btree
end
create_table "recipes", force: :cascade do |t|
t.string "name"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_foreign_key "ingredients", "ingredient_types"
add_foreign_key "recipe_ingredients", "ingredients"
add_foreign_key "recipe_ingredients", "recipes"
add_foreign_key "steps", "recipes"
end
class IngredientType < ApplicationRecord
has_many :ingredients
end
class Ingredient < ApplicationRecord
belongs_to :ingredient_type
has_many :recipe_ingredients
has_many :recipes, through: :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipe
belongs_to :ingredient
has_one :ingredient_type, through: :ingredient
end
class Step < ApplicationRecord
belongs_to :recipe
end
class Recipe < ApplicationRecord
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
has_many :steps
end
Here are my two models
class Comment < ActiveRecord::Base
belongs_to :phone
end
class Phone < ActiveRecord::Base
has_many :comments, :dependent => :destroy
end
Here is the schema for the tables
ActiveRecord::Schema.define(version: 20131119231249) do
create_table "comments", force: true do |t|
t.string "username"
t.string "ipaddy"
t.text "pcomments"
t.string "company"
t.string "calltype"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "pnumber"
t.string "source"
end
create_table "phones", force: true do |t|
t.integer "pnumber"
t.text "mrcomment"
t.integer "ccount"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Here is the raw SQL that works
SELECT phones.ccount ,
comments.*
FROM phones
INNER JOIN comments
ON phones.pnumber = comments.pnumber;
When I run the following in my controller
#phones = Phone.select("phones.ccount, comments.*").joins(:comments).where(:comments => {comments.pnumber => phones.pnumber})
I get the following error
undefined local variable or method `comments' for #<FrontPageController:0x00000003c56c70>
Any help on what the active record statement should like would be greatly appreciated
It seems like you're using the select() erroneously. Have you read the docs: http://apidock.com/rails/ActiveRecord/QueryMethods/select ?
from docs: "Second: Modifies the SELECT statement for the query so that only certain fields are retrieved:"
The query's syntax should more look like (using a standard example):
l = Location.where(["id = ?", id]).select("name, website, city").first.