@user = User.new
@user.id
возвращает ноль, но мне нужно знать это перед сохранением. Является ли это возможным ?
@user = User.new
@user.id
возвращает ноль, но мне нужно знать это перед сохранением. Является ли это возможным ?
Нет, вы не можете получить идентификатор перед сохранением. Идентификационный номер берется из базы данных, но база данных не назначит идентификатор, пока вы не позвоните save
. Все это, конечно, при условии, что вы используете ActiveRecord.
Да, ты можешь!
У меня был тот же вопрос, и я исследовал документы. Возможность решить этот вопрос на самом деле очень зависит от типа вашей базы данных.
В Oracle и Postgresql есть полезные функции, позволяющие легко решить эту проблему. Для MySQL (oracle) или SkySQL (с открытым исходным кодом) это кажется более сложным (но все же возможным). Я бы порекомендовал вам избегать использования этих (MySQL / SkySQL) баз данных, если вам нужны продвинутые инструменты для работы с базами данных.
Во-первых, вы должны попытаться избежать этой ситуации в максимально возможной степени в дизайне приложения, так как опасно играть с идентификаторами, прежде чем они будут сохранены.
Может возникнуть ситуация, когда у вас нет другого выбора: например, когда две таблицы ссылаются на себя, и по соображениям безопасности вы не разрешаете DELETE или UPDATE для этих таблиц.
В этом случае вы можете использовать функцию nextval базы данных (PostgreSQL, Oracle) для генерации следующего идентификационного номера без фактической вставки новой записи.
Используйте его вместе с методом rails find_by_sql.
Чтобы сделать это, например, с postgreSQL и Rails, выберите одну из своих моделей rails и добавьте метод класса (а не метод экземпляра!). Это возможно с помощью слова «self» в начале имени метода. self сообщает Ruby, что этот метод может использоваться только классом, а не его переменными экземпляра (объектами, созданными с помощью 'new').
Моя модель Rails:
class MyToy < ActiveRecord::Base
...
def self.my_next_id_sequence
self.find_by_sql "SELECT nextval('my_toys_id_seq') AS my_next_id"
end
end
Когда вы генерируете таблицу с помощью миграции Rails, по умолчанию Rails автоматически создает столбец с именем id и устанавливает его как таблицу первичного ключа. Чтобы не допустить возникновения «ошибки дублирования первичного ключа», Rails автоматически создает последовательность внутри базы данных и применяет ее к id strong > столбец. Для каждой новой записи (строки), которую вы вставляете в свою таблицу, база данных сама вычисляет, каким будет следующий идентификатор для вашей новой записи.
Rails автоматически присваивает этой последовательности имя с добавлением имени таблицы с помощью «_id_seq». К этой последовательности необходимо применить функцию PostgreSQL nextval, как описано здесь .
Теперь о find_by_sql, как объяснено здесь, он создаст массив , содержащий экземпляры новых объектов вашего класса. Каждый из этих объектов будет содержать все столбцы, генерируемые оператором SQL. Эти столбцы будут появляться в каждом новом экземпляре объекта в виде атрибутов. Даже если этих атрибутов нет в вашей модели класса!
Как вы мудро поняли, наша функция nextval будет возвращать только одно значение. Таким образом, find_by_sql создаст массив, содержащий один экземпляр объекта с одним атрибутом. Чтобы упростить чтение значения этого самого атрибута, мы назовем результирующий столбец SQL «my_next_id», чтобы наш атрибут имел такое же имя.
Ну это все. Мы можем использовать наш новый метод:
my_resulting_array = MyToy.my_next_id_sequence
my_toy_object = my_resulting_array[0]
my_next_id_value = my_toy_object.my_next_id
И используйте его, чтобы решить нашу ситуацию с мертвой блокировкой:
my_dog = DogModel.create(:name => 'Dogy', :toy_id => my_next_id_value)
a_dog_toy = MyToy.new(:my_dog_id => my_dog.id)
a_dog_toy.id = my_next_id_value
a_dog_toy.save
Имейте в виду, если вы не используете my_next_id_value, этот номер идентификатора будет утерян навсегда. (Я имею в виду, что в будущем он не будет использоваться ни одной записью).
База данных не ждет, пока вы ею воспользуетесь. Если где-то в любое время вашему приложению необходимо вставить новую запись в my_table_example (возможно, в то же время, когда мы играем с my_next_id_sequence), база данных всегда будет назначать номер идентификатора этой новой записи сразу после той, которую вы создали с помощью my_next_id_sequence, учитывая, что ваше my_next_id_value зарезервировано. Это может привести к ситуациям, когда записи в my_table_example не будут отсортированы по времени создания.
У меня была похожая ситуация. Я вызвал последовательность, используя find_by_sql в моей модели, которая возвращает массив модели. Я получил идентификатор от первого объекта arry. что-то вроде ниже.
Class User < ActiveRecord::Base
set_primary_key 'user_id'
alias user_id= id=
def self.get_sequence_id
self.find_by_sql "select TEST_USER_ID_SEQ.nextval as contact_id from dual"
end
end
и класс, на который вы ссылаетесь на модель пользователя,
@users = User.get_sequence_id
user = users[0]
from dual
следует отбросить, поскольку Postgres не поддерживает этот синтаксис, но также не требует from для возврата значения. Таким образом, оператор find by sql становится self.find_by_sql "select nextval(TEST_USER_ID_SEQ) as contact_id"
.
- person Paul Pettengill; 05.12.2012
Обычно идентификатор заполняется автоматически из последовательности базы данных.
В рельсах вы можете использовать событие after_create
, которое дает вам доступ к объекту сразу после того, как он был сохранен (и, следовательно, у него есть идентификатор). Это подойдет для большинства случаев.
При использовании Oracle у меня был случай, когда я хотел создать идентификатор самостоятельно (а не использовать последовательность) и в этот пост, я подробно рассказываю, как я это сделал. Короче код:
# a small patch as proposed by the author of OracleEnhancedAdapter: http://blog.rayapps.com/2008/05/13/activerecord-oracle-enhanced-adapter/#comment-240
# if a ActiveRecord model has a sequence with name "autogenerated", the id will not be filled in from any sequence
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
alias_method :orig_next_sequence_value, :next_sequence_value
def next_sequence_value(sequence_name)
if sequence_name == 'autogenerated'
# we assume id must have gotten a good value before insert!
id
else
orig_next_sequence_value(sequence_name)
end
end
end
Хотя это решение специфично для Oracle-расширенного, я предполагаю, что другие базы данных будут иметь аналогичный метод, который вы могли бы переопределить.
Итак, хотя это определенно не рекомендуется, и вы хотите быть абсолютно уверены, почему вы не хотите использовать идентификатор, сгенерированный последовательностью, если он необходим, это определенно возможно. Вот почему я люблю Ruby и Ruby on Rails! :)
В Oracle вы можете получить текущее значение последовательности с помощью этого запроса:
SELECT last_number FROM user_sequences where sequence_name='your_sequence_name';
Итак, в своем классе модели вы можете поместить что-то вроде этого:
class MyModel < ActiveRecord::Base
self.sequence_name = 'your_sequence_name'
def self.my_next_id_sequence
get_data = self.find_by_sql "SELECT last_number FROM user_sequences where sequence_name='your_sequence_name'"
get_data[0].last_number
end
end
И, наконец, в контроллере вы можете получить это значение следующим образом:
my_sequence_number = MyModel.my_next_id_sequence
Таким образом, нет необходимости получать следующее значение с помощью NEXTVAL, и вы не потеряете свой идентификатор.
.nextval
, и да, это будет использовать идентификаторы, если вы их фактически не сохраните. Но этого не избежать.
- person AnoE; 15.01.2016
Что вы можете сделать, так это User.max (id). который вернет наивысший идентификатор в базе данных, вы можете добавить 1. Это ненадежно, хотя может удовлетворить ваши потребности.
Начиная с Rails 5 вы можете просто вызвать next_sequence_value
Примечание: для Oracle, когда установлено self.sequence_name
, запрос следующего значения последовательности создает побочный эффект за счет увеличения значения последовательности.
User.maximum(:id).next
[stackoverflow.com/a/17523916/2231236] - person Nithin   schedule 07.10.2014