التحقق من صحة _uniqueness_of في قضبان النموذج المتداخلة المدمرة
-
03-10-2019 - |
سؤال
لدي نموذج مشروع يقبل سمات متداخلة للمهمة.
class Project < ActiveRecord::Base
has_many :tasks
accepts_nested_attributes_for :tasks, :allow_destroy => :true
end
class Task < ActiveRecord::Base
validates_uniqueness_of :name end
التحقق من صحة التفرد في نموذج المهمة يعطي مشكلة أثناء تحديث المشروع.
في تحرير المشروع ، أحذف مهمة T1 ثم أضف مهمة جديدة بنفس الاسم T1 ، فإن التحقق من صحة التفرد يقيد توفير المشروع.
تجزئة params تبدو شيء مثل
task_attributes => { {"id" =>
"1","name" => "T1", "_destroy" =>
"1"},{"name" => "T1"}}
يتم التحقق من الصحة قبل تدمير المهمة القديمة. وبالتالي فشل التحقق من الصحة.
المحلول
أندرو فرنسا ابتكر رقعة في هذا مسلك, ، حيث يتم التحقق من الصحة في الذاكرة.
class Author
has_many :books
# Could easily be made a validation-style class method of course
validate :validate_unique_books
def validate_unique_books
validate_uniqueness_of_in_memory(
books, [:title, :isbn], 'Duplicate book.')
end
end
module ActiveRecord
class Base
# Validate that the the objects in +collection+ are unique
# when compared against all their non-blank +attrs+. If not
# add +message+ to the base errors.
def validate_uniqueness_of_in_memory(collection, attrs, message)
hashes = collection.inject({}) do |hash, record|
key = attrs.map {|a| record.send(a).to_s }.join
if key.blank? || record.marked_for_destruction?
key = record.object_id
end
hash[key] = record unless hash[key]
hash
end
if collection.length > hashes.length
self.errors.add_to_base(message)
end
end
end
end
نصائح أخرى
كما أفهمها ، لن يكون نهج راينر حول التحقق من التحقق من الذاكرة عمليًا في حالتي ، حيث أن لدي الكثير من "الكتب" و 500 ألف وتنمو. سيكون ذلك نجاحًا كبيرًا إذا كنت ترغب في إحضار كل شيء في الذاكرة.
الحل الذي توصلت إليه هو:
ضع حالة التفرد في قاعدة البيانات (التي وجدتها دائمًا فكرة جيدة ، كما في تجربتي ، لا تقوم Rails دائمًا بعمل جيد هنا) عن طريق إضافة ما يلي ملف الترحيل في DB/Migrate/:
add_index :tasks [ :project_id, :name ], :unique => true
في وحدة التحكم ، ضع حفظ أو update_attributes داخل المعاملة ، وإنقاذ استثناء قاعدة البيانات. على سبيل المثال ،
def update
@project = Project.find(params[:id])
begin
transaction do
if @project.update_attributes(params[:project])
redirect_to(project_path(@project))
else
render(:action => :edit)
end
end
rescue
... we have an exception; make sure is a DB uniqueness violation
... go down params[:project] to see which item is the problem
... and add error to base
render( :action => :edit )
end
end
نهاية
بالنسبة إلى Rails 4.0.1 ، يتم تمييز هذه المشكلة على أنها تم إصلاحها من خلال طلب السحب هذا ، https://github.com/rails/rails/pull/10417
إذا كان لديك جدول به فهرس حقل فريد من نوعه ، وكنت علامة على سجل للتدمير ، وقمت بإنشاء سجل جديد بنفس قيمة الحقل الفريد ، ثم عند الاتصال ، سيتم إلقاء خطأ فريد فريد من نوعه.
شخصيا هذا لا يزال لا يعمل بالنسبة لي ، لذلك لا أعتقد أنه ثابت تماما حتى الآن.
إجابة راينر نعمة جيدة. لكن من الأفضل أن نضع علامة على المهام التي يتم تكرارها.
class Project < ActiveRecord::Base
has_many :tasks, inverse_of: :project
accepts_nested_attributes_for :tasks, :allow_destroy => :true
end
class Task < ActiveRecord::Base
belongs_to :project
validates_each :name do |record, attr, value|
record.errors.add attr, :taken if record.project.tasks.map(&:name).count(value) > 1
end
end
المرجع هذه
لماذا لا تستخدم: النطاق
class Task < ActiveRecord::Base
validates_uniqueness_of :name, :scope=>'project_id'
end
سيؤدي ذلك إلى إنشاء مهمة فريدة لكل مشروع.