Ruby on rails 验证外键约束
我有两个表Ruby on rails 验证外键约束,ruby-on-rails,ruby,foreign-keys,Ruby On Rails,Ruby,Foreign Keys,我有两个表数据元素和类型。用于存储静态类型列表的类型,有两列:id和name数据元素具有类型列,该列应在类型表中引用id。当通过HTML表单创建DataElement时,类型列表显示为下拉列表,一切正常。我需要通过api调用创建DataElement,我想验证作为参数传递给REST调用的type实际上是存储在types表中的类型之一。我还没能想出办法。如有任何建议,我将不胜感激。模型方式(带验证) 如果您想在模型方面处理这个问题,我建议您创建一个“自定义属性”,这样您就不会影响现有类型字段的现有
数据元素
和类型
。用于存储静态类型列表的类型,有两列:id
和name
<代码>数据元素具有类型
列,该列应在类型
表中引用id
。当通过HTML表单创建DataElement
时,类型列表显示为下拉列表,一切正常。我需要通过api调用创建DataElement
,我想验证作为参数传递给REST调用的type
实际上是存储在types
表中的类型之一。我还没能想出办法。如有任何建议,我将不胜感激。模型方式(带验证)
如果您想在模型方面处理这个问题,我建议您创建一个“自定义属性”,这样您就不会影响现有类型
字段的现有功能。大致如下:
app/models/data\u element.rb
def type_name=(value)
found_type = Type.find_by(name: value.to_s)
self.type = found_type unless found_type.nil?
end
def type_name
type.try(:name)
end
def create
@data_element = DataElement.new(data_element_params)
@data_element.save
end
def data_element_params
# Please ensure that `type` is not present in `permit`
params.require(:data_element).permit(:whatever, :type_name, :other_field)
end
class Type
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
def create
# Please add an index to `name` column and ensure is a unique one
type = Type.find_by(name: params[:type].to_s)
if type.nil?
head :unprocessable_entity
else
@data_element = DataElement.new(data_element_params.merge(type: type))
if @data_element.save
#dosomething
end
end
end
class Type < ActiveRecord::Base
has_many :data_elements
end
class DataElement < ActiveRecord::Base
belongs_to :type, foreign_key: 'de_type'
end
app/controllers/data\u elements\u controller.rb
def type_name=(value)
found_type = Type.find_by(name: value.to_s)
self.type = found_type unless found_type.nil?
end
def type_name
type.try(:name)
end
def create
@data_element = DataElement.new(data_element_params)
@data_element.save
end
def data_element_params
# Please ensure that `type` is not present in `permit`
params.require(:data_element).permit(:whatever, :type_name, :other_field)
end
class Type
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
def create
# Please add an index to `name` column and ensure is a unique one
type = Type.find_by(name: params[:type].to_s)
if type.nil?
head :unprocessable_entity
else
@data_element = DataElement.new(data_element_params.merge(type: type))
if @data_element.save
#dosomething
end
end
end
class Type < ActiveRecord::Base
has_many :data_elements
end
class DataElement < ActiveRecord::Base
belongs_to :type, foreign_key: 'de_type'
end
通过这种方式,您可以像处理属性一样处理type_name
。您甚至不需要验证:如果没有找到该名称的类型
,您只需不更改该值(您实际上可以报告错误,甚至在任何情况下都可以进行验证)
显然,如果你也使用类型
,那么类似@data\u element.update\u属性(type\u name:'foo',type:type.first)
你会得到奇怪的结果(类型
赋值之后是类型。first
而不是名为'foo'
)的类型,因此,如果您在作业中使用type\u name
,请确保type
在那一刻没有使用,但不应该是问题,只是不要将其放入permit
参数中
控制器方式
我的第一个建议是“不要使用类型”作为表/模型名。type
列通常由Rails用于单表继承,这是主要原因。即使将其命名为DataElementType也可以,但无论如何,这是您的选择
关于你的问题,有很多方法可以做到这一点,我想到的第一个方法是
app/models/type.rb
def type_name=(value)
found_type = Type.find_by(name: value.to_s)
self.type = found_type unless found_type.nil?
end
def type_name
type.try(:name)
end
def create
@data_element = DataElement.new(data_element_params)
@data_element.save
end
def data_element_params
# Please ensure that `type` is not present in `permit`
params.require(:data_element).permit(:whatever, :type_name, :other_field)
end
class Type
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
def create
# Please add an index to `name` column and ensure is a unique one
type = Type.find_by(name: params[:type].to_s)
if type.nil?
head :unprocessable_entity
else
@data_element = DataElement.new(data_element_params.merge(type: type))
if @data_element.save
#dosomething
end
end
end
class Type < ActiveRecord::Base
has_many :data_elements
end
class DataElement < ActiveRecord::Base
belongs_to :type, foreign_key: 'de_type'
end
app/controllers/data\u elements\u controller.rb
def type_name=(value)
found_type = Type.find_by(name: value.to_s)
self.type = found_type unless found_type.nil?
end
def type_name
type.try(:name)
end
def create
@data_element = DataElement.new(data_element_params)
@data_element.save
end
def data_element_params
# Please ensure that `type` is not present in `permit`
params.require(:data_element).permit(:whatever, :type_name, :other_field)
end
class Type
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
def create
# Please add an index to `name` column and ensure is a unique one
type = Type.find_by(name: params[:type].to_s)
if type.nil?
head :unprocessable_entity
else
@data_element = DataElement.new(data_element_params.merge(type: type))
if @data_element.save
#dosomething
end
end
end
class Type < ActiveRecord::Base
has_many :data_elements
end
class DataElement < ActiveRecord::Base
belongs_to :type, foreign_key: 'de_type'
end
您可能想对该方法进行一点重构,但这肯定会给您提供一个总体思路。我同意不建议使用类型,因为它通常由STI使用。在您的情况下,如果您不想在types
表中存储太多数据,我建议您定义一个常量,如ELEMENT\u types
,它是一个数组,包含您将在DataElement
模型中使用的所有类型。然后添加一个验证,如
class DataElement < ActiveRecord::Base
validates :type, inclusion: { in: ELEMENT_TYPES }`
end
然后用types\u container
helper方法替换container
参数。如果要转换类型的值,这应该是您的选择。如果这是您所需要的,您可能需要了解rails中的一些东西。
希望我的回答能有所帮助
更新:
好吧,如果你坚持使用db来存储你的数据,这并不难满足你的需要。
我假设一个类型
有许多数据元素
,您的模式如下所示:
ActiveRecord::Schema.define(version: 20150708021148) do
create_table "data_elements", force: :cascade do |t|
t.integer "de_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
(您可能需要在de_type
上添加索引或类似内容以提高查询效率)
首先,在数据元素和类型模型中定义关联关系:
app/models/type.rb
def type_name=(value)
found_type = Type.find_by(name: value.to_s)
self.type = found_type unless found_type.nil?
end
def type_name
type.try(:name)
end
def create
@data_element = DataElement.new(data_element_params)
@data_element.save
end
def data_element_params
# Please ensure that `type` is not present in `permit`
params.require(:data_element).permit(:whatever, :type_name, :other_field)
end
class Type
validates :name, presence: true, uniqueness: { case_sensitive: false }
end
def create
# Please add an index to `name` column and ensure is a unique one
type = Type.find_by(name: params[:type].to_s)
if type.nil?
head :unprocessable_entity
else
@data_element = DataElement.new(data_element_params.merge(type: type))
if @data_element.save
#dosomething
end
end
end
class Type < ActiveRecord::Base
has_many :data_elements
end
class DataElement < ActiveRecord::Base
belongs_to :type, foreign_key: 'de_type'
end
如果type
表的id集合中不存在de_type
,则此验证将使更新或创建失败
我将在rails控制台中向您展示它的工作原理:
2.1.1 :006 > Type.all
=> #<ActiveRecord::Relation [
#<Type id: 1, name: "type1", created_at: "2015-07-08 02:17:33", updated_at: "2015-07-08 02:17:33">,
#<Type id: 2, name: "type2", created_at: "2015-07-08 02:17:38", updated_at: "2015-07-08 02:17:38">,
#<Type id: 3, name: "type3", created_at: "2015-07-08 02:17:39", updated_at: "2015-07-08 02:17:39">,
#<Type id: 4, name: "type4", created_at: "2015-07-08 02:17:41", updated_at: "2015-07-08 02:17:41">,
#<Type id: 5, name: "type5", created_at: "2015-07-08 02:17:43", updated_at: "2015-07-08 02:17:43">
]>
如您所见,我试图创建一个数据元素,其de\u type
为7。你找不到id为7的类型
记录,对吗?因此,您最终看到出现了RecordInvalid错误,错误信息为类型不能为空
现在,让我们创建一个没有任何错误的数据元素:
2.1.1 :010 > DataElement.create!(de_type: 2)
(0.1ms) begin transaction
Type Load (131.0ms) SELECT "types".* FROM "types" WHERE "types"."id" = ? LIMIT 1 [["id", 2]]
SQL (1.1ms) INSERT INTO "data_elements" ("de_type", "created_at", "updated_at") VALUES (?, ?, ?) [["de_type", 2], ["created_at", "2015-07-08 03:05:51.046949"], ["updated_at", "2015-07-08 03:05:51.046949"]]
(6.3ms) commit transaction
=> #<DataElement id: 4, de_type: 2, created_at: "2015-07-08 03:05:51", updated_at: "2015-07-08 03:05:51">
2.1.1:010>DataElement.create!(德鲁类型:2)
(0.1ms)开始事务处理
类型加载(131.0ms)从“类型”中选择“类型”。*其中“类型”。“id”=?限制1[[“id”,2]]
SQL(1.1ms)插入“数据元素”(“数据类型”、“创建时间”、“更新时间”)值(?,?)[“数据类型”,2],“创建时间”,“2015-07-08 03:05:51.046949”],[“更新时间”,“2015-07-08 03:05:51.046949”]
(6.3ms)提交事务
=> #
那么,记录被成功创建了,有趣吗?
您可以在rails指南中找到更多信息(很抱歉,我不能发布超过2个链接)谢谢!列的实际名称为de_类型。我想我在描述模型时尽量保持简单。这绝对有帮助。我在想,一定有一种方法可以让它通过使用“验证”或仅仅通过定义对象之间的关系(有一个或属于)来自动工作。我想不是。我会按照你的建议去做。有一种方法是肯定的,不是完全自动的,但会使用验证,让我更新答案;)@火龙多尔,伊芙梦想家,你们真是太棒了!非常感谢。我和Ruby打交道才一个星期,所以我非常感谢所有额外的细节。现在,为什么我的名字是“user3038783”?我最好改一下:-)谢谢。您是否建议使用常量元素类型,而不是将其存储在表中或用表中的数据填充。?我更希望它存储在数据库中,以防它被其他东西引用。