একাধিক বিদেশী কীগুলির সাথে রেল সমিতি


84

সম্পর্কের সংজ্ঞা দেওয়ার জন্য আমি একটি টেবিলের দুটি কলাম ব্যবহার করতে সক্ষম হতে চাই। সুতরাং উদাহরণ হিসাবে একটি টাস্ক অ্যাপ্লিকেশন ব্যবহার।

চেষ্টা 1:

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :owner, class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

তাহলে Task.create(owner_id:1, assignee_id: 2)

এটি আমাকে সঞ্চালন করতে দেয় Task.first.ownerযা কোন ব্যবহারকারীকে প্রত্যাবর্তন করে এবং Task.first.assigneeযা ব্যবহারকারীকে দুটি প্রদান করে কিন্তু User.first.taskকিছুই প্রত্যাবর্তন করে না। কোনটি কারণ কোনও ব্যবহারকারীর অন্তর্ভুক্ত নয়, সেগুলি মালিক এবং অ্যাসিগ্নির অন্তর্ভুক্ত । সুতরাং,

চেষ্টা 2:

class User < ActiveRecord::Base
  has_many :tasks, foreign_key: [:owner_id, :assignee_id]
end

class Task < ActiveRecord::Base
  belongs_to :user
end

দুটি বিদেশী কী সমর্থিত বলে মনে হচ্ছে না বলে এটি পুরোপুরি ব্যর্থ।

তাই আমি যা চাই তা বলতে সক্ষম হোন User.tasks ব্যবহারকারীদের মালিকানা এবং নির্ধারিত কার্য উভয়ই এবং ।

মূলত একরকম একটি সম্পর্ক তৈরি করুন যা এর প্রশ্নের সাথে সমান হবে Task.where(owner_id || assignee_id == 1)

এটা কি সম্ভব?

হালনাগাদ

আমি ব্যবহার করতে চাইছি না finder_sql, তবে এই ইস্যুটির অগ্রহণযোগ্য উত্তরটি আমি যা চাই তার কাছাকাছি মনে হচ্ছে: কারাগারে - একাধিক সূচি কী সমিতি

সুতরাং এই পদ্ধতিটি দেখতে এইভাবে হবে,

চেষ্টা 3:

class Task < ActiveRecord::Base
  def self.by_person(person)
    where("assignee_id => :person_id OR owner_id => :person_id", :person_id => person.id
  end 
end

class Person < ActiveRecord::Base

  def tasks
    Task.by_person(self)
  end 
end

যদিও আমি এটিটিতে কাজ করতে পারি তবে আমি Rails 4নিম্নলিখিত ত্রুটিটি পেয়ে যাচ্ছি:

ActiveRecord::PreparedStatementInvalid: missing value for :owner_id in :donor_id => :person_id OR assignee_id => :person_id

4
আপনি কি সন্ধান করছেন এই রত্নটি? github.com/composite-primary-keys/composite_primary_keys
mus

তথ্য মিউজিকের জন্য ধন্যবাদ তবে এটি আমি খুঁজছি না। আমি উভয় বা কলামের প্রদত্ত মান হিসাবে একটি কোয়েরি চাই। যৌগিক প্রাথমিক কী নয়।
JonathanSimmons

হ্যাঁ, আপডেটটি এটি পরিষ্কার করে তোলে। মণির কথা ভুলে যাও আমরা দুজনেই ভেবেছিলাম আপনি কেবল একটি রচিত প্রাথমিক কীটি ব্যবহার করতে চান। এটি কমপক্ষে একটি কাস্টম স্কোপ একটি স্কোপড সম্পর্ককে সংজ্ঞায়িত করে সম্ভব হওয়া উচিত। আকর্ষণীয় দৃশ্য। আমি এটি পরে একবার দেখে নেব
dre-hh

এফডব্লিউআইডাব্লু আমার লক্ষ্য এখানে একটি নির্দিষ্ট ব্যবহারকারীদের টাস্ক পাওয়া এবং অ্যাক্টিভেকর্ড :: রিলেশন ফর্ম্যাটটি ধরে রাখা যাতে আমি অনুসন্ধান / ফিল্টারিংয়ের ফলাফলের জন্য টাস্ক স্কোপগুলি ব্যবহার চালিয়ে যেতে পারি।
JonathanSimmons

উত্তর:


80

টিএল; ডিআর

class User < ActiveRecord::Base
  def tasks
    Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
  end
end

সরান has_many :tasksমধ্যে Userবর্গ।


আমাদের টেবিলে has_many :tasksনামকরণের কোনও কলাম নেই বলে ব্যবহার করা মোটেই অর্থহীন নয় ।user_idtasks

আমার ক্ষেত্রে সমস্যাটি সমাধান করতে আমি যা করেছি তা হ'ল:

class User < ActiveRecord::Base
  has_many :owned_tasks,    class_name: "Task", foreign_key: "owner_id"
  has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end

class Task < ActiveRecord::Base
  belongs_to :owner,    class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
  # Mentioning `foreign_keys` is not necessary in this class, since
  # we've already mentioned `belongs_to :owner`, and Rails will anticipate
  # foreign_keys automatically. Thanks to @jeffdill2 for mentioning this thing 
  # in the comment.
end

এইভাবে, আপনি User.first.assigned_tasksপাশাপাশি কল করতে পারেন User.first.owned_tasks

এখন, আপনি একটি পদ্ধতি নামক বর্ণনা করতে পারেন tasksযে আয় সমন্বয় assigned_tasksএবং owned_tasks

পঠনযোগ্যতা যতদূর যায় এটি একটি ভাল সমাধান হতে পারে তবে পারফরম্যান্সের দৃষ্টিকোণ থেকে এটি পাওয়ার জন্য এখনকার মতো এতটা ভাল হবে না tasks , একবারের পরিবর্তে দুটি প্রশ্ন জারি করা হবে এবং তারপরে, ফলাফলটি এই দুটি প্রশ্নের মধ্যে পাশাপাশি যোগদান করা দরকার।

সুতরাং কোনও ব্যবহারকারীর অন্তর্ভুক্ত কাজগুলি পেতে, আমরা ক্লাসে একটি কাস্টম tasksপদ্ধতি Userনিম্নলিখিত উপায়ে সংজ্ঞায়িত করব :

def tasks
  Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end

এইভাবে, এটি একক ক্যোয়ারিতে সমস্ত ফলাফল আনবে এবং আমাদের কোনও ফলাফল মার্জ বা একত্রিত করতে হবে না।


এই সমাধান মহান! এটি যথাযথভাবে নির্ধারিত, মালিকানাধীন এবং সমস্ত কার্যগুলিতে পৃথক অ্যাক্সেসের প্রয়োজনীয়তা বোঝায়। একটি বড় প্রকল্পে এমন কিছু হ'ল দুর্দান্ত ভবিষ্যদ্বাণী হবে। আমার ক্ষেত্রে এটি অবশ্য প্রয়োজন ছিল না। has_many"বুদ্ধি না করার জন্য " যদি আপনি গৃহীত উত্তরটি পড়ে থাকেন তবে আপনি দেখতে পাবেন যে আমরা কোনও has_manyঘোষণা ব্যবহার করেই শেষ করি নি । আপনার প্রয়োজনীয়তাগুলি প্রতিটি অন্যান্য দর্শকের প্রয়োজনের সাথে মিলে যায় তা অনুকরনীয়, এই উত্তরটি আরও কম বিচারযোগ্য হতে পারে।
JonathanSimmons

এই কথাটি বলে আমি বিশ্বাস করি যে এটি এই দীর্ঘমেয়াদী সেটআপ হ'ল আমাদের এই সমস্যাযুক্ত লোকদের পক্ষে পরামর্শ দেওয়া উচিত। যদি আপনি অন্যান্য উত্তরগুলির একটি মৌলিক উদাহরণ এবং কার্য পদ্ধতির সম্মিলিত সেট পুনরুদ্ধার করার জন্য ব্যবহারকারীর পদ্ধতির অন্তর্ভুক্ত করতে আপনার উত্তরটি সংশোধন করেন তবে আমি এটিকে নির্বাচিত উত্তর হিসাবে সংশোধন করব। ধন্যবাদ!
JonathanSimmons

হ্যা আমি আপনার সাথে একমত. ব্যবহার owned_tasksএবং assigned_tasksসমস্ত কার্য পেতে একটি কার্যকারিতা ইঙ্গিত থাকবে। যাইহোক, আমি আমার উত্তর আপডেট করেছি এবং Userসমস্ত সম্পর্কিত সম্পর্কিত ক্লাসে একটি পদ্ধতি অন্তর্ভুক্ত করেছি tasks, এটি একটি ক্যোয়ারিতে ফলাফল এনে দেবে, এবং কোনও মার্জিং / সংমিশ্রণ করার দরকার নেই।
আরসলান আলী

4
কেবলমাত্র একটি এফওয়াইআই, Taskমডেলটিতে উল্লিখিত বিদেশী কীগুলি আসলে প্রয়োজনীয় নয়। যেহেতু আপনার belongs_toসম্পর্কগুলির নামকরণ করা হয়েছে :ownerএবং তাই :assignee, রেলগুলি ডিফল্টরূপে ধরে নেবে যে বিদেশী কীগুলির নামকরণ করা হয়েছে owner_idএবং assignee_idযথাক্রমে।
jeffdill2

4
@ আরসলানআলি কোন সমস্যা নেই :-)
jeffdill2

48

উপরে @ dre-hh এর উত্তরের উপর প্রসারিত হওয়া, যা আমি আর ails টি ক্ষেত্রে প্রত্যাশার মতো কাজ করতে দেখিনি It এটি উপস্থিত হয় 5 রেলের মধ্যে এখন একটি ডিফল্ট অন্তর্ভুক্ত রয়েছে যেখানে এর প্রভাবের ধারাটি রয়েছে WHERE tasks.user_id = ?, যা নেই বলে ব্যর্থ হয়েছে ailsuser_id এই পরিস্থিতিতে কলাম না ।

আমি খুঁজে পেয়েছি এটি এখনও কোনও has_manyসমিতির সাথে কাজ করা সম্ভব , আপনি কেবল এই অতিরিক্তটি অনস্কোপ করতে হবে যেখানে রেলগুলি দ্বারা যুক্ত করা হয়েছে।

class User < ApplicationRecord
  has_many :tasks, ->(user) {
    unscope(:where).where(owner: user).or(where(assignee: user)
  }
end

ধন্যবাদ @ ডুইট, আমি এই বিষয়টি কখনই ভাবিনি!
গ্যাবে কোপলি

belongs_toএটির জন্য কাজ করতে পারে না (যেখানে পিতামাতার কোনও আইডি নেই, সুতরাং এটি মাল্টি-কলাম পিকে ভিত্তিক হতে হবে)। এটি কেবল বলেছে যে সম্পর্কটি শূন্য (এবং কনসোলটির দিকে তাকিয়ে আমি লাম্বদা কোয়েরি কখনই কার্যকর করা দেখতে পাচ্ছি না)।
fgblomqvist

@fgblomqvist সম্পর্কিত_তে একটি বিকল্প রয়েছে: প্রাথমিক_কি, যা সমিতির জন্য ব্যবহৃত সম্পর্কিত অবজেক্টের প্রাথমিক কীটি প্রদান করে এমন পদ্ধতিটি নির্দিষ্ট করে। ডিফল্টরূপে এটি আইডি। আমি মনে করি এটি আপনাকে সাহায্য করতে পারে।
আহমেদ কামাল

@ আহমদকামাল আমি কি দেখতে পাচ্ছি না যে এটি কীভাবে কার্যকর?
fgblomqvist

24

পাখি 5:

আপনার যদি ডিফল্ট আনসকোপ করা দরকার যেখানে ক্লজটি @ ডাইট উত্তরটি দেখতে চাইলে আপনি যদি এখনও একটি has_many এসোসিয়েটোন চান।

যদিও User.joins(:tasks)আমাকে দেয়

ArgumentError: The association scope 'tasks' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.

এটি আর সম্ভব না হওয়ায় আপনি @ আরস্লান আলি সমাধানটিও ব্যবহার করতে পারেন।

রেল 4:

class User < ActiveRecord::Base
  has_many :tasks, ->(user){ where("tasks.owner_id = :user_id OR tasks.assignee_id = :user_id", user_id: user.id) }
end

আপডেট ১: জনাথনসিমন্স মন্তব্য সম্পর্কে

ব্যবহারকারীর মডেলটির ব্যবহারকারীর অবজেক্টটি স্কোপে পাস করার পরে মনে হচ্ছে পিছনের দিকের পদ্ধতির মতো

আপনাকে এই সুযোগে ব্যবহারকারী মডেলটি পাস করতে হবে না। বর্তমান ব্যবহারকারীর উদাহরণটি এই ল্যাম্বডায় স্বয়ংক্রিয়ভাবে পাস হবে passed এটিকে কল করুন:

user = User.find(9001)
user.tasks

আপডেট 2:

যদি সম্ভব হয় তবে আপনি কি উত্তরটি প্রসারিত করতে পারেন যা ঘটছে তা ব্যাখ্যা করার জন্য? আমি এটি আরও ভালভাবে বুঝতে চাই যাতে আমি অনুরূপ কিছু বাস্তবায়ন করতে পারি। ধন্যবাদ

has_many :tasksঅ্যাক্টিভেকর্ড ক্লাসে কল করা কিছু বর্গ ভেরিয়েবলের মধ্যে একটি ল্যাম্বডা ফাংশন সংরক্ষণ করবে এবং tasksএটি তার অবজেক্টে কোনও পদ্ধতি উত্পন্ন করার এক অভিনব উপায় , যা এই ল্যাম্বডাকে কল করবে। উত্পন্ন পদ্ধতিটি নিম্নলিখিত সিউডোকোডের মতো দেখাবে:

class User

  def tasks
   #define join query
   query = self.class.joins('tasks ON ...')
   #execute tasks_lambda on the query instance and pass self to the lambda
   query.instance_exec(self, self.class.tasks_lambda)
  end

end

4
এত দুর্দান্ত, অন্য কোথাও আমি দেখেছি লোকেরা যৌগিক কীগুলির জন্য এই পুরানো রত্নটি ব্যবহার করার পরামর্শ দেওয়ার চেষ্টা করতে থাকে
ফায়ারড্রাগন

4
সম্ভব হলে কি এই উত্তরটি প্রসারিত করতে পারছেন যা ঘটছে তা ব্যাখ্যা করার জন্য? আমি এটি আরও ভালভাবে বুঝতে চাই যাতে আমি অনুরূপ কিছু বাস্তবায়ন করতে পারি। ধন্যবাদ
স্টিফেন সীসা

4
যদিও এটি সমিতিটি ধরে রাখে, এটি অন্যান্য সমস্যাগুলির কারণ হয়। দস্তাবেজগুলি থেকে: দ্রষ্টব্য: এই সমিতিগুলিতে যোগদান, আগ্রহী লোডিং এবং প্রিলোডিং পুরোপুরি সম্ভব নয়। এই ক্রিয়াকলাপগুলি উদাহরণস্বরূপ সৃষ্টির আগে ঘটে এবং সুযোগটি শূন্যতার সাথে যুক্ত করা হবে। এটি অপ্রত্যাশিত আচরণের দিকে পরিচালিত করতে পারে এবং হ্রাস করা হয়। api.rubyonrails.org/class/ActtiveRecord/Associations/…
s2t2

4
এটি আশাব্যঞ্জক বলে মনে হচ্ছে তবে রেল ends টি সমাপ্তির পরে কেবল লাম্বদা ব্যবহার না করে একটি whereক্যোয়ারী দিয়ে বিদেশী_কি ব্যবহার করছে
মোহাম্মদ এল মহল্লভী

4
হ্যাঁ, এটি প্রদর্শিত হবে যেন এই আর পাগল 5. কাজ করে
ডোয়াইট

13

আমি এর জন্য একটি সমাধান কাজ করেছি। আমি কীভাবে এটি আরও উন্নত করতে পারি তার কোনও নির্দেশকের জন্য আমি উন্মুক্ত।

class User < ActiveRecord::Base

  def tasks
    Task.by_person(self.id)
  end 
end

class Task < ActiveRecord::Base

  scope :completed, -> { where(completed: true) }   

  belongs_to :owner, class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"

  def self.by_person(user_id)
    where("owner_id = :person_id OR assignee_id = :person_id", person_id: user_id)
  end 
end

এটি মূলত has_many সমিতিকে ওভাররাইড করে তবে এখনও এটি প্রদান করে still ActiveRecord::Relation আমি অবজেক্টটি ।

সুতরাং এখন আমি এই জাতীয় কিছু করতে পারি:

User.first.tasks.completed এবং ফলাফলটি সমস্ত সমাপ্ত টাস্কটির মালিকানাধীন বা প্রথম ব্যবহারকারীর কাছে নির্ধারিত।


আপনি এখনও আপনার প্রশ্ন সমাধানের জন্য এই পদ্ধতি ব্যবহার করছেন? আমি একই নৌকায় আছি এবং ভাবছি যে আপনি কোনও নতুন উপায়ে শিখেছেন বা যদি এটি এখনও সেরা বিকল্প হয়।
ড্যান

এখনও আমি খুঁজে পেয়েছি সেরা বিকল্প।
JonathanSimmons

এটি সম্ভবত পুরানো প্রচেষ্টার একটি অবশিষ্টাংশ। এটি মুছে ফেলার জন্য উত্তর সম্পাদনা করেছেন।
JonathanSimmons

4
এই এবং নীচে dre-hh এর উত্তর কি একই জিনিস অর্জন করবে?
dkniffin

4
> ব্যবহারকারীর মডেলটির ব্যবহারকারীর অবজেক্টটি স্কোপে পাস করা পিছনের দিকে যাওয়ার মত মনে হয়। আপনাকে কোনও কিছু পাস করতে হবে না, এটি একটি ল্যাম্বডা এবং বর্তমান ব্যবহারকারীর উদাহরণটি এটিতে স্বয়ংক্রিয়ভাবে পাস হবে
dre-hh

2

অ্যাসোসিয়েশন এবং রেলপথে (একাধিক) বিদেশী কীগুলিতে আমার উত্তর (৩.২): কীভাবে মডেলটিতে সেগুলি বর্ণনা করা যায় এবং মাইগ্রেশন লিখতে হয় তা কেবল আপনার জন্য!

আপনার কোড হিসাবে, এখানে আমার পরিবর্তন আছে

class User < ActiveRecord::Base
  has_many :tasks, ->(user) { unscope(where: :user_id).where("owner_id = ? OR assignee_id = ?", user.id, user.id) }, class_name: 'Task'
end

class Task < ActiveRecord::Base
  belongs_to :owner, class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

সতর্কতা: আপনি যদি রেলসডমিন ব্যবহার করছেন এবং নতুন রেকর্ড তৈরি করতে বা বিদ্যমান রেকর্ড সম্পাদনা করতে চান তবে দয়া করে আমার পরামর্শ মতো করবেন না B কারণ আপনি যখন এই জাতীয় কিছু করেন তখন এই হ্যাকটি সমস্যার সৃষ্টি করবে:

current_user.tasks.build(params)

কারণটি হ'ল রেলগুলি কার্য_ইউজার.আইডিকে টাস্ক.ইউসার_আইডি পূরণ করার চেষ্টা করবে, কেবল এটির জন্য ইউজার_আইডের মতো কিছুই নেই।

সুতরাং, আমার হ্যাক পদ্ধতিটি বাক্সের বাইরের উপায় হিসাবে বিবেচনা করুন, তবে এটি করবেন না।


নিশ্চিত নয় যে এই উত্তরটি অনুমোদিত উত্তরটি ইতিমধ্যে দেয় নি এমন কিছু সরবরাহ করে। তদ্ব্যতীত, এটি অন্য প্রশ্নের বিজ্ঞাপন হিসাবে মনে হচ্ছে।
JonathanSimmons

@ জোনাথানসিমন্স আপনাকে ধন্যবাদ বিজ্ঞাপনের মতো কাজ করা খারাপ হলে আমি এই উত্তরটি সরিয়ে ফেলব।
সানসোফট

2

যেহেতু রেল 5 রয়েছে আপনি এটিও করতে পারেন যা অ্যাক্টিভেকর্ড নিরাপদ উপায়:

def tasks
  Task.where(owner: self).or(Task.where(assignee: self))
end
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.