I have models:
Category:
class Category < ActiveRecord::Base
has_many :entities, as: :resourcable
end
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.text :short_descr
t.text :full_descr
t.timestamps
end
end
end
Language:
class Language < ActiveRecord::Base
has_many :entities, as: :resourcable, dependent: :destroy
validates :code, uniqueness: true
end
class CreateLanguages < ActiveRecord::Migration
def change
create_table :languages do |t|
t.string :name
t.string :code
t.timestamps
end
end
end
And Entity:
class Entity < ActiveRecord::Base
belongs_to :language
belongs_to :resourcable, polymorphic: true
end
class CreateEntities < ActiveRecord::Migration
def change
create_table :entities do |t|
t.integer :language_id
t.string :name
t.text :short_descr
t.text :full_descr
t.references :resourcable, polymorphic: true
t.timestamps
end
end
end
In Categories there are default values for fields (short_descr, full_descr), In Entities there are translations for this fields. I need to render as json all Categories with appropriate translations: at first, I need to take Language with appropriate code (for example ru), next, I need to find all language Entities for this language, next, if Entity have filled short_descr and full_descr I need to render Category with this values, else I need to render the Category with default values (this values in Categories table). How to do this? I prefer ActiveRecord buy consider pure SQL.
EDITED
Now I'm trying to use gem 'squeel':
Language.joins{entities.category}.
select{coalesce(entities.short_descr, categories.short_descr)}.
where{languages.code == 'en'}
but it doesn't work (undefined methodshort_descr' for nil:NilClass`). There is the problem?
Entity.joins(:language, :category).
select('categories.*, coalesce(entities.short_descr, categories.short_descr) as short_descr,
coalesce(entities.full_descr, categories.full_descr) as full_descr').
where('languages.code = ?', 'en')
Related
I want to know the best way to have an uniqueness constraint enforced on two related model attributes in rails that are both no primary keys
class Parent > ApplicationRecord
has_many :children
:name
end
class Child > ApplicationRecord
:name
end
I want to enforce that (parent.name, child.name) is unique for every parent. e.g.
(parent1, child1) and (parent2, child1) is allowed
(parent1, child1) and (parent1, child1) is a violation
Ideally, I would enforce this in Postgres, however I have only seen the option to add uniqueness constraints on multiple columns of the same table.
Alternatively, I have written a custom validator for rails that does what I want, but this is cumbersome. There needs to be a better solution...
For completeness, here is the constraints validator which requires one to add a children function to a model returning the list of children.
class NamePairValidator < ActiveModel::Validator
def validate(record)
record.children.values.each do |model_children|
names = model_children.to_a.collect {|model| model.name}
if (names.select{|name| names.count(name) > 1 }.size > 0)
record.errors[:name] << 'Path leading to this resource has no unique name'
end
end
end
end
(in Parent.rb)
def children
{children: :children}
end
Migrations:
class CreateDomains < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name
t.string :domain_type
t.timestamps
end
end
end
class CreateSubjects < ActiveRecord::Migration[5.0]
def change
create_table :subjects do |t|
t.string :name
t.string :subject_type
t.timestamps
end
end
end
class CreateJoinTableDomainSubject < ActiveRecord::Migration[5.0]
def change
create_join_table :domains, :subjects do |t|
t.index [:domain_id, :subject_id]
t.index [:subject_id, :domain_id]
end
end
end
I just used similar one in my code
validates :child_name, uniqueness: { scope: :parent_id }
More..
(i) https://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
(ii) Validate uniqueness of multiple columns
Insipered by the-has-many-through-association of the official doc of ruby on rails:
class CreateAppointments < ActiveRecord::Migration[5.0]
def change
create_table :domains do |t|
t.string :name, null: false
t.string :domain_type
t.timestamps
end
create_table :subjects do |t|
t.string :name, null: false
t.string :subject_type
t.timestamps
end
create_table :fields do |t|
t.belongs_to :domain, index: true
t.belongs_to :subject, index: true
t.timestamps
end
end
end
Note
I took the initative to rename your model JoinTableDomainSubject by Field to be more readable.
I also force name field not be nil to check uniqueness.
(adding null: false in migrations files and validates :name, presence: true in both models)
Now the dedicated classes:
class Subject < ApplicationRecord
has_many :fields
has_many :domains, through: :fields
validates :name, presence: true
end
class Domain < ApplicationRecord
has_many :fields
has_many :subjects, through: :fields
validates :name, presence: true
end
class Field < ApplicationRecord
belongs_to :domain
belongs_to :subject
validate :domain_and_subject_names_uniqueness
private
def domain_and_subject_names_uniqueness
if class.includes(:domain, subject)
.where(domain: { name: domain.name }, subject: { name: subject.name })
.exists?
errors.add :field, 'duplicity on names'
end
end
end
Since the models are associated, I can use Field.first.domain to access Domain model of a given Field and vice versa.
I have two models 'Tutorial' and 'Tutorialcategory'
class Tutorialcategory < ActiveRecord::Base
has_many :tutorials
class Tutorial < ActiveRecord::Base
belongs_to :tutorialcategory
Tutorials are associated with multiple categories like html,rubyonrails where html and ruby on rails are tutorialcategories
followings are migrations
class CreateTutorials < ActiveRecord::Migration
def change
create_table :tutorials,force: true do |t|
t.string :title
t.text :body
t.integer :rating
t.string :videoid
t.belongs_to :tutorialcategory
t.timestamps
end
end
end
class CreateTutorialcategories < ActiveRecord::Migration
def change
create_table :tutorialcategories do |t|
t.string :title
t.timestamps null:false
end
end
end
All tutorials are listing properly on index page but when I see category page it gives me following error
PG::Error: ERROR: column tutorials.tutorialcategory_id does not exist
I have no idea why you named your model Tutorialcategory instead of TutorialCategory which follow Rails naming convention and make it easier to understand.
Firstly, rollback your db one step
rake db:rollback
Change your migration file to:
class CreateTutorials < ActiveRecord::Migration
def change
create_table :tutorials,force: true do |t|
t.string :title
t.text :body
t.integer :rating
t.string :videoid
t.belongs_to :tutorial_category, index: true
t.timestamps
end
end
end
class CreateTutorialCategories < ActiveRecord::Migration
def change
create_table :tutorial_categories do |t|
t.string :title
t.timestamps null:false
end
end
end
Run the migration again and edit your model to match with new schema.
class TutorialCategory < ActiveRecord::Base
has_many :tutorials
class Tutorial < ActiveRecord::Base
belongs_to :tutorial_category
In my web app, I have nodes and links. A link has two nodes. One node is a source node, and the other node is a target node. Basically, I want source and target columns in the database that hold references to nodes. I am trying to figure out how to implement this.
Here is the migration for the nodes model:
class CreateNodes < ActiveRecord::Migration
def change
create_table :nodes do |t|
t.string :name
t.integer :group
t.references :link, index: true
t.timestamps
end
end
end
Here is the node model:
class Nodes < ActiveRecord::Base
belongs_to :link
end
I am trying to figure out how to set up the migration for the links model. Here is what I have so far:
class CreateLinks < ActiveRecord::Migration
def change
create_table :links do |t|
t.integer :value
t.boolean :checked
t.timestamps
end
end
end
Here is what I have in my model:
class Links < ActiveRecord::Base
has_many :nodes
end
Would the correct migration look like this?
class CreateLinks < ActiveRecord::Migration
def change
create_table :links do |t|
t.integer :value
t.boolean :checked
t.references :source
t. references :target
t.timestamps
end
end
end
t.references :smith is basically a shortcut for t.integer :smth_id so if your Nodes belong to Links, then yes that construction seems correct.
not sure where your links#source and links#target point to though.
Sorry, i'am newbie
I have database:
Migrate
-Mst_group tble
class CreateMstGroups < ActiveRecord::Migration
def self.up
create_table :mst_groups do |t|
t.string :group_name
end
end
end
-Mst_japan
class CreateMstJapans < ActiveRecord::Migration
def self.up
create_table :mst_japans do |t|
t.string :name_level
end
end
end
-Tbl_user
class CreateTblUsers < ActiveRecord::Migration
def self.up
create_table :tbl_users do |t|
t.string :login_name, :null =>false,:limit =>15
t.string :password,:null =>false,:limit =>50
t.string :full_name,:null =>false
t.string :full_name_kana
t.string :email,:null =>false
t.string :tel,:null =>false,:limit =>15
t.date :birthday,:null =>false
t.references :mst_groups
end
end
end
-Tbl_detail_user_japan
class CreateTblDetailUserJapans < ActiveRecord::Migration
def self.up
create_table :tbl_detail_user_japans do |t|
t.date :start_date
t.date :end_date
t.integer :total
t.references :tbl_users
t.references :mst_japans
end
end
end
Model
class MstGroup < ActiveRecord::Base
has_many :tbl_users
end
class MstJapan < ActiveRecord::Base
has_many :tbl_detail_user_japans
end
class TblUser < ActiveRecord::Base
belongs_to :mst_group
has_one :tbl_detail_user_japan
end
class TblDetailUserJapan < ActiveRecord::Base
belongs_to :tbl_user
belongs_to :mst_japan
end
Controller
def index
#user= ???????
end
How to write command select : login_name, full_name, full_name_kana, email, tel, group_name, name_lever, start_date, end_date, total in controller
It depends on how you want to retrieve the User object. You need to tell Rails how to find the TblUser object. If, for example, the user ID is known, let's say in a variable called 'id' then you would do:
def index
#user=TblUser.find(id)
end
It depends on your application logic how Rails would know which user you need. You may need an input from the user in case of log in, etc.
(Typically in Rails you would call the table 'Users', by convention tables and classes have the same name and then you wouldn't need to call the class TblUser)
That is all you need in the controller, you don't need to tell it which fields you want.
Then in the View you can access all the fields:
Fields on TblUser, example:
<%= #user.email %>
You can access the fields from related objects through the relations, example:
<%= #user.mst_group.group_name %>
Hope that helps to get you started.
I'm trying to figure out if there is a way to do this in Rails more efficiently.
There is kind of a long setup for the question, so please bear with me.
Let's say I have models Customer, Phone, Address
Here are sample migrations to give you an idea:
class CreatePhones < ActiveRecord::Migration
def self.up
create_table :phones do |t|
t.integer :country_prefix, :limit => 3
t.integer :area_prefix, :limit => 5
t.integer :number, :limit => 7
t.integer :category_id
t.references :phonable, :polymorphic => true
t.timestamps
end
end
end
class CreateAddress < ActiveRecord::Migration
def self.up
create_table :addresses do |t|
t.string :address_line_1
t.string :address_line_2
t.string :address_line_3
t.string :city
t.string :state
t.string :zip
t.string :country
t.string :attn
t.integer :category_id
t.references :addressable, :polymorphic => true
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.string :name
t.string :code
t.integer :category_id # Every subcategory has a category: i.e. phone has work, fax,mobile
t.timestamps
end
end
end
class CreateCustomers < ActiveRecord::Migration
def self.up
create_table :customers do |t|
t.string :code , :limit => 20 , :null => false
t.string :name , :null => false
t.string :billing_name
t.integer :preferred_shipping_method_id
end
end
Here are models and relations:
class Customer < ActiveRecord::Base
belongs_to :preferred_shipping_method , :class_name => "Category", :foreign_key => :preferred_shipping_method_id
has_many :addresses, :as => :addressable, :include => :category, :dependent => :destroy
has_many :phones, :as => :phonable, :include => :category, :dependent => :destroy
end
class Category < ActiveRecord::Base
has_many :addresses
has_many :phones
has_many :customer_by_shipping_methods, :class_name => "Customer", :foreign_key => :preferred_shipping_method_id
has_many :subcategories, :class_name => "Category", :foreign_key => :category_id
belongs_to :category, :class_name => "Category"
end
class Address < ActiveRecord::Base
belongs_to :category
belongs_to :addressable, :polymorphic => true
end
class Phone < ActiveRecord::Base
belongs_to :category
belongs_to :phonable, :polymorphic => true
end
Here is a question.
Let's say I have a customer record with a bunch of phone (mobile, work) and addresses (billing, shipping).
old = Customer.where(:code => "ABC").first
Then I'm creating or importing (from a legacy DB) another customer object
new = Customer.new
new.code = "ABC"
new.phones.build(:number => "12345567")
etc.
Then I want to compare old customer info to the new customer info and based on that update old customer info.
Like so :
if old.eql?(new) # this should compare not only, name & code and such but also polymorphic associations
old.update_with(new) # this should update old info attributes with new if there is new info, or if update / add to one of the associations
old.save #
else
new.save
end
So the question is is there any CONVENTIONAL way in Rails 3 to do what I describe in comments?
Right now I'm overriding hash & eql? methods which is fine for comparison. But to update each attribute and each associated object and its attributes, is getting kind of involved. I was wondering if there is an easier way to do this then my way:
class Customer < ActiveRecord::Base
def hash
%{#{ name }#{ code }}.hash # There is a lot more here of course
end
def eql?(other)
hash == other.hash
end
def update_with(other)
name = other.name
code = other.code
etc ....
end
end
Ok it doesn't look like there is a standard solution so here is something I came up if anybody else is looking for it.
You can rename methods anyway you like, just put this in you lib folder in some .rb file. ( don't forget to mention it in environment.rb like so require 'custom_comparisons'
/lib/custom_comparisons.rb
module ActiveRecord
class Base
def comparison_hash
h = ""
self.attributes.each_pair do |key, value|
unless ["id", "updated_at", "created_at"].include?(key)
h << "#{value}"
end
end
h.hash
end
def eql_to?(other)
comparison_hash == other.comparison_hash
end
def differences_from?(other)
h = {}
self.attributes.each_pair do |key, value|
unless self.method_missing(key) == other.method_missing(key)
h[key.to_sym] = [self.method_missing(key), other.method_missing(key)]
end
end
h
end
end
end
This can be cleaned up a bit and I need to add association drill down but the solution is there.
This does the comparison and the shows the differences between objects. Now I can update attributes that need to be updated. Will add update method and drill down tomorrow.