Rails: создайте уникальный автоинкрементный идентификатор на основе записей одного уровня - PullRequest
1 голос
/ 11 июня 2019

У меня есть три модели в моем проекте рельсов, а именно Пользователь, Игра, Матч

пользователь может создать множество матчей для каждой игры поэтому структура таблицы для совпадений выглядит как

            table name: game_matches

+----+---------+---------+-------------+------------+
| id | user_id | game_id | match_type  | match_name |
+----+---------+---------+-------------+------------+
|  1 |       1 |       1 | practice    |            |
|  2 |       3 |       2 | challenge   |            |
|  3 |       1 |       1 | practice    |            |
|  4 |       3 |       2 | challenge   |            |
|  5 |       1 |       1 | challenge   |            |
|  6 |       3 |       2 | practice    |            |
+----+---------+---------+-------------+------------+

я хочу создать match_name на основе значений user_id, game_id и match_type

for example match_name should be create like below


+----+---------+---------+-------------+-------------+
| id | user_id | game_id | match_type  | match_name  |
+----+---------+---------+-------------+-------------+
|  1 |       1 |       1 | practice    | Practice 1  |
|  2 |       3 |       2 | challenge   | Challenge 1 |
|  3 |       1 |       1 | practice    | Practice 2  |
|  4 |       3 |       2 | challenge   | Challenge 2 |
|  5 |       1 |       1 | challenge   | Challenge 1 |
|  6 |       3 |       2 | practice    | Practice 1  |
+----+---------+---------+-------------+-------------+

Как мне получить это автоинкрементное значение в моей модели рельсов при создании новой записи. Любые предложения помощи приветствуются.

Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 11 июня 2019

Я вижу два способа решения этой проблемы:

  • БД: триггер
  • Rails: обратный вызов

Триггер (при условии Postgres):

  DROP TRIGGER IF EXISTS trigger_add_match_name ON customers;
  DROP FUNCTION IF EXISTS function_add_match_name();
  CREATE FUNCTION function_add_match_name()
  RETURNS trigger AS $$
  BEGIN
    NEW.match_name := (
      SELECT
      CONCAT(game_matches.match_type, ' ', COALESCE(count(*), 0))
      FROM game_matches
      WHERE game_matches.user_id = NEW.user_id AND game_matches.match_type = NEW.match_type
    );
    RETURN NEW;
  END
  $$ LANGUAGE 'plpgsql';

  CREATE TRIGGER trigger_add_match_name
  BEFORE INSERT ON game_matches
  FOR EACH ROW
  EXECUTE PROCEDURE function_add_match_name(); 

Обратите внимание, что это не проверено.

Рельсы

class GameMatch
  before_create :assign_match_name

  private

  def assign_match_name
   number = GameMatch.where(user_id: user_id, match_type: match_type).count || 0
   name = "#{match_type} #{number + 1}"
   self.match_name = name
  end
end

Опять не проверено.

Я бы предпочел триггерное решение, поскольку обратные вызовы могут быть пропущены или вообще пропущены при вставке через чистый SQL.

Кроме того, я бы добавил столбец «match_number» вместо полного имени, а затем создал бы имя внутри модели, декоратора или помощника представления (более гибкое, I18n), но логика осталась прежней.

0 голосов
/ 11 июня 2019

Вы должны получить последние match_name для этих user и game, разделить их, увеличить счетчик и объединить с пробелом. К сожалению, SQL не предоставляет функции SPLIT, поэтому, как показано ниже, будет хорошим началом:

SELECT match_name
FROM match_name
WHERE user_id = 3
  AND game_id = 2
ORDER BY id DESC
LIMIT 1

На самом деле лучше создать столбец match_number типа INT, чтобы сохранить число по типу , а создать имя путем конкатенации типа с этим номером.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...