I have a habtm relationship between Dish and DailyMenu. Here is a scope
scope :menu_available, -> (type_id, daily_menu_id){ where('dish_type_id = ?', type_id) & where(joins(:daily_menus).where.not('daily_menu_id = ?', daily_menu_id)) }
It intends to find dishes of some type but that are not currently included in menu. Sadly it does not work correctly. Need some help please
A DishType model
class DishType < ActiveRecord::Base
include DishTypeSetter
enum meal:[:main_course, :second_course, :drink]
end
Dish model
class Dish < ActiveRecord::Base
has_and_belongs_to_many :daily_menus
has_many :orders
has_many :users, through: :orders
belongs_to :dish_type
scope :main_meals, -> { joins(:dish_type).where('meal = ?', 0) }
scope :second_meals, -> { joins(:dish_type).where('meal = ?', 1) }
scope :drinks, -> { joins(:dish_type).where('meal = ?', 2) }
#scope :menu_available, -> (type_id, daily_menu_id){ where('dish_type_id = ?', type_id) & where(joins(:daily_menus).where.not('daily_menu_id = ?', daily_menu_id)) }
scope :menu_available, lambda { |type_id, daily_menu_id|
where(joins(:daily_menus).where('daily_menu_id != ? AND dish_type_id = ?', daily_menu_id, type_id))
}
validates :dish_type, :name, presence: true
validates :name, uniqueness: { scope: :dish_type_id }
end
Menu model
class DailyMenu < ActiveRecord::Base
has_and_belongs_to_many :dishes
has_many :orders
has_many :users, through: :orders
end
Also about DishType model - it only has has_many dishes association(I moved this to a concern)/ But it does not have has_many daily_menus/
create_table "daily_menus", force: :cascade do |t|
t.string "day"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.date "date"
end
add_index "daily_menus", ["day"], name: "index_daily_menus_on_day", unique: true, using: :btree
create_table "daily_menus_dishes", force: :cascade do |t|
t.integer "daily_menu_id"
t.integer "dish_id"
end
create_table "dish_types", force: :cascade do |t|
t.integer "meal"
end
create_table "dishes", force: :cascade do |t|
t.string "name"
t.integer "type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "dish_type_id"
end
add_index "dishes", ["dish_type_id", "name"], name: "index_dishes_on_dish_type_id_and_name", unique: true, using: :btree
scope :menu_available, lambda { |type_id, daily_menu_id|
joins(:daily_menus)
.where('dishes.dish_type_id = ? AND daily_menus.id != ?', type_id, daily_menu_id)
}
Related
I'm creating a rails application in which a user can create a group, add contacts, add a contact to that group and subsequently broadcast information out to the users to a group they have created.
I'm at the third stage where I'm now trying to allow the logged in user to add a contact to the group.
I have three models for many to many relationships:
class UserGroups < ApplicationRecord
belongs_to :user
belongs_to :group
end
class Group < ApplicationRecord
belongs_to :user
has_many: :user_groups
has_many: :users, through: :user_groups
validates :title, presence: true
end
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_one_attached :avatar
has_many :groups, dependent: :destroy
has_many :posts, dependent: :destroy
has_many :user_groups
has_many :users, through: :user_groups
before_create :set_circleid
has_many :contactships, dependent: :destroy
has_many :contacts, -> { where contactships: { status: :accepted }}, through: :contactships
has_many :requested_contacts, -> { where contactships: { status: :requested }}, through: :contactships, source: :contact
has_many :pending_contacts, -> { where contactships: { status: :pending }}, through: :contactships, source: :contact
has_many :blocked_contacts, -> { where contactships: { status: :blocked }}, through: :contactships, source: :contact
has_many :contactships_inverse, class_name: 'Contactship', foreign_key: :contact_id
has_many :contacts_inverse, through: :contactships_inverse, source: :user
def all_contacts
contacts + contacts_inverse
end
def has_contactship?(contact)
#return true if the user is a contact
return true if self == contact
contactships.map(&:contact_id).include?(contact.id)
end
def requested_contacts_with?(contact)
return false if self == contact
#we are going to map requested contacts with list of users to see if they include contact_id
requested_contacts.map(&:id).include?(contact.id)
end
def pending_contacts_with?(contact)
return false if self == contact
pending_contacts.map(&:id).include?(contact.id)
end
def contacts_with?(contact)
return false if self == contact
contacts.map(&:id).include?(contact.id)
end
def contact_request(contact)
#unless the contact is not equal to self and contactship does not already exist
unless self == contact || Contactship.where(user: self, contact: contact).exists?
#transaction means that if one fails they both are rolled back
transaction do
#for user to another user (sent request)
Contactship.create(user: self, contact: contact, status: :pending)
#from another user to user (recieve request)
Contactship.create(user: contact, contact: self, status: :requested)
end
end
def accept_request(contact)
transaction do
Contactship.find_by(user: self, contact: contact, status: [:requested])&.accepted!
Contactship.find_by(user: contact, contact: self, status: [:pending])&.accepted!
end
end
def reject_request(contact)
transaction do
Contactship.find_by(user: self, contact: contact)&.destroy!
Contactship.find_by(user: contact, contact: self)&.destroy!
end
end
end
And a method within my group controller (not sure what to do here):
#for adding a user to a group?
def add_user
#search for the group?
#group = Group.find(params[:id])
#add a user to that group via user_groups? How?
end
schema.rb:
ActiveRecord::Schema.define(version: 2020_06_22_142356) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.bigint "record_id", null: false
t.bigint "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.bigint "byte_size", null: false
t.string "checksum", null: false
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "groups", force: :cascade do |t|
t.string "title"
t.bigint "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_groups_on_user_id"
end
create_table "contactships", force: :cascade do |t|
t.bigint "user_id"
t.bigint "contact_id"
t.integer "status", limit: 2, default: 0
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["contact_id"], name: "index_contactships_on_contact_id"
t.index ["user_id"], name: "index_contactships_on_user_id"
end
create_table "posts", force: :cascade do |t|
t.string "description"
t.integer "user_id"
t.string "thought"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "user_groups", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "group_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["group_id"], name: "index_user_groups_on_group_id"
t.index ["user_id"], name: "index_user_groups_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "first_name"
t.string "last_name"
t.string "groupid"
t.text "bio"
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "groups", "users"
add_foreign_key "comments", "users"
add_foreign_key "user_groups", "groups"
add_foreign_key "user_groups", "users"
end
How would I go about developing the method so I can successfully add a contact to a group in the console? I'm confused particularly about the method to make that happen, especially because the contacts are not their own model but part of the user model.
Thanks!
You could do
group = Group.find(params[:id])
contact = User.find(params[:user_id])
UserGroups.create(user: contact, group: group)
Naming
You already mention the term contacts in your question, maybe consider naming your association like this. You can specify a class_name attribute to let Rails know the name of the your model class if it doesn't match the association name.
belongs_to :owner, class_name: "User"
has_many: :user_groups
has_many: :contacts, through: :user_groups, class_name: "User"
validates :title, presence: true
https://guides.rubyonrails.org/association_basics.html#options-for-belongs-to
has_many through vs. has_and_belongs_to
You should think also if you really need the UserGroups model or use a has_and_belongs_to association, see from the Rails guide
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you'll need to remember to create the joining table in the database).
https://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many
I'm working with a legacy database that gives the following schema for the tables product and familia_producto (rake db:schema:dump)
create_table "producto", primary_key: "barcode", force: true do |t|
t.string "codigo_corto", limit: 16, null: false
t.string "marca", limit: 35
t.string "descripcion", limit: 50
t.string "contenido", limit: 10
t.string "unidad", limit: 10
t.float "stock", default: 0.0
t.float "precio"
t.float "precio_neto"
t.float "costo_promedio"
t.float "fifo"
t.float "vendidos"
t.boolean "aplica_iva"
t.integer "otros_impuestos", limit: 2
t.integer "familia", limit: 2, default: 1, null: false
t.integer "id_tipo_producto", limit: 2, null: false
t.boolean "es_perecible"
t.float "dias_stock", default: 1.0
t.float "margen_promedio", default: 0.0
t.boolean "es_venta_fraccionada"
t.float "stock_pro", default: 0.0
t.float "tasa_canje", default: 1.0
t.float "precio_mayor"
t.float "cantidad_mayor"
t.boolean "es_mayorista"
t.boolean "estado"
t.boolean "precio_editable"
end
create_table "familia_producto", force: true do |t|
t.string "nombre", limit: 32, null: false
end
In the models I've this
class FamiliaProducto < ActiveRecord::Base
self.table_name = 'familia_producto'
has_many :productos, :class_name => 'Producto', :primary_key => 'barcode', :foreign_key => 'familia'
end
class Producto < ActiveRecord::Base
self.table_name = 'producto'
belongs_to :familia_producto, :class_name => 'FamiliaProducto'
end
But when I call the .familia the producto object throws me a number, not the FamiliaProducto object.
2.1.0 :012 > p = Producto.all[0]
Producto Load (1.7ms) SELECT "producto".* FROM "producto"
=> #<Product......
2.1.0 :013 > p.familia
=> 2
That 2 should be the FamiliaProducto object.
You must use the name of the association, also need to add the foreign key to the belongs_to
class Producto < ApplicationRecord
belongs_to :familia_producto, class_name: 'FamiliaProducto', foreign_key: 'familia'
end
class FamiliaProducto < ApplicationRecord
has_many :productos, class_name: 'Producto', foreign_key: 'familia'
end
p = Producto.first
# Returns a FamiliaProducto object
p.familia_producto
# Returns an integer
p.familia
I have two tables with a join table in between:
Series
create_table "series", force: :cascade do |t|
t.string "title", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
class Series < ApplicationRecord
include PgSearch::Model
has_many :makers
has_many :creators, through: :makers
has_many :sources, dependent: :destroy
has_many :entries, through: :sources
validates :title, presence: true
pg_search_scope :search_by_title, against: {
title: 'A',
title_en: 'B',
title_en_jp: 'C'
}
end
Maker
create_table "makers", force: :cascade do |t|
t.bigint "series_id", null: false
t.bigint "creator_id", null: false
t.bigint "creator_type", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["creator_id"], name: "index_makers_on_creator_id"
t.index ["series_id", "creator_id", "creator_type"], name: "index_makers_on_series_and_creator_and_type", unique: true
t.index ["series_id"], name: "index_makers_on_series_id"
end
# frozen_string_literal: true
class Maker < ApplicationRecord
extend Enumerize
belongs_to :series
belongs_to :creator
validates :series, uniqueness: { scope: %i[creator creator_type] }
enumerize :creator_type, in: {
author: 1,
artist: 2
}, predicates: true, scope: true
end
Creator
create_table "creators", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["name"], name: "index_creators_on_name"
end
class Creator < ApplicationRecord
include PgSearch::Model
has_many :makers
has_many :series, through: :makers
pg_search_scope :search_by_title, against: :name
end
Based on the tables above, I wanted to create a where method, which would find Series for specific makers. The issue is for series that have two makers, usually an author and artist.
So while this code works, to find a Series for a specific creator:
def self.find_by_creators(title, creators)
where(title: title)
.joins(makers: :creator)
.where(
makers: {
creator_type: :author,
creators: { name: creators[:authors] }
}
)
end
when trying to add another where, nothing gets returned:
def self.find_by_creators(title, creators)
where(title: title)
.joins(makers: :creator)
.where(
makers: {
creator_type: :author,
creators: { name: creators[:authors] }
}
)
.where(
makers: {
creator_type: :artist,
creators: { name: creators[:artists] }
}
)
end
Here is my table structure
class CreateStudentAssignments < ActiveRecord::Migration[5.1]
def change
create_table :student_assignments do |t|
t.string :title
t.string :subject
t.text :description
t.float :amount, default: 0
t.date :start_date
t.date :due_date
t.integer :word_count
t.time :approximate_estimated_duration
t.boolean :active, default: true
t.float :overall_rating
t.integer :view_count, default: 0
t.boolean :assignment_approved, default: false
t.references :assignment_status, foreign_key: true
t.references :student, foreign_key: { to_table: :users }
t.references :difficulty_level, foreign_key: true
t.references :writer, foreign_key: { to_table: :users } # Assignment accepted writer
t.timestamps
end
end
end
While try to create a record
p = {title: "title", subject: "subject", description: "des", amount: 5.00, start_date: Date.today + 1, due_date: Date.today + 5, word_count: 100, approximate_estimated_duration: 1000, overall_rating: 4, view_count:4, assignment_status: AssignmentStatus.last, difficulty_level_id: 4, student: User.last}
StudentAssignment.create(p).errors
ActiveModel::MissingAttributeError: can't write unknown attribute `student_id`
from (irb):56
How to pass that user?
Thanks in advance
When i try to update foreign_key for table BusinessCard. It auto update the Company table without needed.
My Console Image
Can someone help me with this. Thanks
Edit---
My Controller
def update
#bc = BusinessCard.find_by(business_card_id: params[:id], deleted: false)
raise "名刺情報は存在しておりません。" unless #bc
raise "同じ名刺情報が存在しております。" if (#bc.name != params[:name] or #bc.email != params[:email] or #bc.company.name != params[:c_name]) and BusinessCard.joins(:company).where(name: params[:name], email: params[:email], deleted: 0, 'companies.name' => params[:c_name]).exists?
ActiveRecord::Base.transaction do
##bc.name = params[:name]
##bc.email = params[:email]
##bc.tel = params[:tel]
##bc.furigana = params[:furigana]
##bc.recieve_date = params[:recieve_date]
##bc.update_by = #current_user.user_id
#Company
#bc.company_id = bz_company if params[:c_name] and params[:c_post_code]
#department
##bc.department_id = bz_department if params[:d_name]
raise #bc.errors unless #bc.save
#images
#bz_omt if params[:i_omt]
#bz_ura if params[:i_ura]
end
render_json(#bc, :ok)
end
def bz_company
#company = Company.find_by(name: params[:c_name], post_code: params[:c_post_code])
#company = Company.new(name: params[:c_name], post_code: params[:c_post_code], create_by: #current_user.user_id) unless #company
#company.address = params[:c_address]
#company.email = params[:c_email]
#company.tel = params[:c_tel]
#company.fax = params[:c_fax]
#company.url = params[:c_url]
#company.deleted = 0
#company.update_by = #current_user.user_id
raise #company.errors unless #company.save
#company.company_id
end
BusinessCard Model
class BusinessCard < ApplicationRecord
#Association
#With Tag
has_many :map_tags, primary_key: 'business_card_id', foreign_key: 'business_card_id'
has_many :tags, :through => :map_tags
#with Comment
has_many :map_comments, primary_key: 'business_card_id', foreign_key: 'business_card_id'
has_many :comments, :through => :map_comments
#with Company
has_one :company, primary_key: 'company_id', foreign_key: 'company_id'
#with department
has_one :department, primary_key: 'department_id', foreign_key: 'department_id'
#with file_locations
has_many :file_locations, primary_key: 'business_card_id', foreign_key: 'business_card_id'
end
Company Model
class Company < ApplicationRecord
#Association
has_many :business_cards, primary_key: 'company_id', foreign_key: 'company_id'
end
Company Migration
class CreateCompanies < ActiveRecord::Migration[5.0]
def change
create_table :companies, id: false do |t|
t.column :company_id, 'INTEGER PRIMARY KEY AUTOINCREMENT'
t.string :name, limit: 150, null: false
t.text :address, limit: 1000, null: false
t.string :email, limit: 129
t.string :tel, limit: 20
t.string :fax, limit: 20
t.string :url, limit: 150
t.boolean :deleted, default: false
t.integer :create_by
t.integer :update_by
t.timestamps
end
end
end
BusinessCard Migration
class CreateBusinessCards < ActiveRecord::Migration[5.0]
def change
create_table :business_cards, id: false do |t|
t.column :business_card_id, 'INTEGER PRIMARY KEY AUTOINCREMENT'
t.string :name, limit: 50, null: false
t.string :furigana, limit: 50
t.string :email, limit: 129, null: false
t.string :tel, limit: 20, null: false
t.integer :owner_id
t.datetime :recieve_date
t.integer :company_id, null: false
t.integer :department_id, null: false
t.boolean :deleted, default: false
t.integer :create_by
t.integer :update_by
t.timestamps
end
add_index :business_cards, :business_card_id, :unique => true
end
end
Try this:
instead this in BusinnesCard Model
has_one :company, primary_key: 'company_id', foreign_key: 'company_id'
put this
belongs_to :company, primary_key: 'company_id', foreign_key: 'company_id'
PS Your code is little diry, i recommend you to replace primary_keys. For example, instead company_id, use id
You are using primary key column names that do not follow Rails' conventions. Therefore you have to tell Rails the names with primary_key= in your models:
# in app/models/company.rb
self.primary_key = 'company_id'
# in app/models/business_card.rb
self.primary_key = 'business_card_id'