【问题标题】:How can I make my seeds correctly working?我怎样才能让我的种子正常工作?
【发布时间】:2023-03-16 05:07:01
【问题描述】:

我还是 Ruby / Rails 的新手,在运行我的命令 rails db:seed

之前,我需要你的帮助以确保我的种子是好的

这是我的种子:

require 'faker'

puts "Creating 10 fake user heros..."
10.times do
  user = User.create!(
    username: Faker::DcComics.hero,
    password: "123456",
    email: "#{user.username}@gmail.com",
    bio: "Hi, I'm #{user.username}. Faker::Movie.quote",
    avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
  )
  puts "#{user.username} created!"
end

puts "Creating 10 fake hero services..."
10.times do
  service = Service.create!(
    name: "#{Faker::Verb.ing_form} service",
    description: "I'll use my #{Faker::Superhero.power} to accomplish the mission",
    address: "#{Faker::Address.street_address}, #{Faker::Address.city}",
    photos: Faker::LoremFlickr.image(size: "900x900", search_terms: ["#{Faker::Verb.ing_form}"]),
    user_id: rand(user.id)
  )
  puts "#{service.name} created!"
end

puts "Finished!"

我想我会有几个问题,尤其是:

  • 我的插值:我不确定我是否可以将它与 Faker 一起使用
  • 我想使用usernameemail 中给出的相同用户名,这样做对吗?
  • 我的用户模型在 DB 表中没有头像但 has_one_attached :avatar,我可以给它一个种子吗?
  • 关于我的服务模型的相关问题 (has_many_attached :photos)。如何为它生成多张图片?
  • 我想从上面创建的一个随机user_id 给我的服务模型,我真的不知道我该如何继续...

【问题讨论】:

  • 您有开发和测试数据库,如果出现问题可以删除和重新创建。试试看!
  • 嗨 Schwern,我做到了!我试图发射我的种子,但我收到了rails abortedmessage,但我不知道为什么
  • rails aborted 附带一条错误消息,其中包含调用了哪些方法以获取错误的大堆栈跟踪。 Rails 内部有很多东西。忽略那个。查看代码中的部分,例如 db/seed.rbapp/models/user.rb,然后忽略其余部分。可能是undefined method `username' for nil:NilClass

标签: ruby-on-rails ruby


【解决方案1】:

我的插值:我不确定我是否可以将它与 Faker 一起使用

插值适用于任何表达式。甚至是多个陈述。 "#{ a = 1 + 1; b = 2 + 2; a + b}" 将产生 "6"。但不要那样做,这会令人困惑。

name: "#{Faker::Verb.ing_form} service",

等价于

name: Faker::Verb.ing_form.to_s + " service"

在一些地方你错过了插值。

bio = "Hi, I'm #{user.username}. Faker::Movie.quote"

应该是……

bio = "Hi, I'm #{user.username}. #{Faker::Movie.quote}"

在其他地方不需要插值。

search_terms: ["#{Faker::Verb.ing_form}"]

Faker::Verb.ing_form 已经返回一个字符串,不需要插值。

search_terms: [Faker::Verb.ing_form]

我想使用用户名和电子邮件中给出的相同用户名,我这样做对吗?

不,您不能引用user,直到它已经被创建。如果你尝试,你会得到类似undefined method `username' for nil:NilClass 的东西。 usernilnil 没有名为 username 的方法。

您可以创建一个用户,设置其电子邮件,然后保存。

user = User.new(
  username: Faker::DcComics.hero,
  password: "123456",
  avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
)
user.email = "#{user.username}@gmail.com"
user.bio = "Hi, I'm #{user.username}. #{Faker::Movie.quote}"
user.save!

或者您可以先创建用户名并将其存储在变量中以供参考。

  username = Faker::DcComics.hero
  user = User.create!(
    username: username,
    password: "123456",
    email: "#{username}@gmail.com",
    bio: "Hi, I'm #{username}. #{Faker::Movie.quote}",
    avatar: Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics'])
  )

我的用户模型在 DB 表中没有头像,但是 has_one_attached :avatar,我可以给它一个种子吗?

我对@9​​87654321@ 不是很熟悉,但我认为它应该像你写的那样工作。


我想从上面创建的其中一个中为我的服务模型提供一个随机 user_id,我真的不知道我该如何继续...

简单的是使用sample

user = User.all.sample

我们可以使用pluck 仅获取 ID 来改善这一点。

user = User.pluck(:id).sample

但是,这会加载所有用户。对于小型测试数据库来说,这不是问题。对于生产代码,这是一种巨大的浪费。相反,我们可以使用order by random() limit 1。在 Rails 6 中它有点笨重。

User.order(Arel.sql('RANDOM()')).pluck(:id).first

请参阅this answer 以获得解释。

请注意,如果您已经加载了用户,请传递用户,而不是其 ID。这避免了服务必须再次加载用户。

10.times do
  user = User.order(Arel.sql('RANDOM()')).pluck(:id).first
  service = Service.create!(
    ...
    user: user
  )
end

但是有更好的方法...


关于我的服务模型的相关问题(has_many_attached :photos)。如何为它生成多张图片?

在这里,我们实现了真正的飞跃。不要生成静态测试数据,而是使用工厂。 FactoryBot 可以轻松定义“工厂”以使用 Faker 生成测试数据。你已经成功了一半。而FactoryBotRails 让 FactoryBot 了解如何使用 Rails 模型。

用户工厂看起来像这样。

FactoryBot.define do
  factory :user do
    username { Faker::DcComics.hero }
    password { "123456" }
    email { "#{username}@gmail.com" }
    bio { "Hi, I'm #{username}. #{Faker::Movie.quote}" }
    avatar { Faker::LoremFlickr.image(size: "900x900", search_terms: ['dccomics']) }
  end
end

请注意,我们可以根据username 创建一个email。属性可以引用其他属性。那是因为属性实际上是有点像这样的小函数:

class UserFactory do
  def username
    @username ||= Faker::DDcComics.hero
  end

  def email
    @email ||= "#{username}@gmail.com"
  end
end

然后制作 10 个……

users = FactoryBot.create_list(:user, 10)

您可以覆盖默认值。如果您想要具有特定电子邮件地址的用户...

user = FactoryBot.create(:user, email: "c.kent@dailyplanet.com")

由于您可以即时创建用户,因此您不再需要播种特定数据。

现在服务。让我们看一下Service工厂。要创建服务,我们需要照片。

  factory :service do
    name { "#{Faker::Verb.ing_form} service" }
    description { "I'll use my #{Faker::Superhero.power} to accomplish the mission" }
    address { "#{Faker::Address.street_address}, #{Faker::Address.city}" }
    user
    # photos is presumably has-many_attached and so takes an Array.
    photos {
      [
       Faker::LoremFlickr.image(
         size: "900x900",
         search_terms: [Faker::Verb.ing_form]
       )
      ]
    }
  end

看看user。 FactoryBot 将自动填写使用您的用户工厂创建的用户。解决了用随机用户创建服务的问题。

我们的最后一步是通过DRY 定义我们的工厂来制作照片。

factory :photo, class: Faker::LoremFlickr do
  size { "900x900" }
  search_terms { Faker::Lorem.words }

  initialize_with do
    Faker::LoremFlickr.image(attributes)
  end
end

这不是 Rails 模型,所以我们需要通过向 FactoryBot 提供其类和how to initialize it 来教它如何制作照片。 attributes 是一个包含 size 和 search_terms 的 Hash。

另外请注意,我使用了更通用的搜索词。需要特定搜索词的工厂将提供自己的搜索词。

我们可以制作任意数量的照片。

photos = FactoryBot.build_list(:photos, 3)

使用照片工厂,我们可以干掉用户和服务工厂。一切都在这里。

FactoryBot.define do
  factory :photo, class: Faker::LoremFlickr do
    size { "900x900" }
    search_terms { Faker::Lorem.words }

    initialize_with do
      Faker::LoremFlickr.image(attributes)
    end
  end

  factory :user do
    username { Faker::DcComics.hero }
    password { "123456" }
    email { "#{username}@gmail.com" }
    bio { "Hi, I'm #{username}. #{Faker::Movie.quote}" }
    avatar { build(:photo, search_terms: ['dccomics']) }
  end

  factory :service do
    name { "#{Faker::Verb.ing_form} service" }
    description { "I'll use my #{Faker::Superhero.power} to accomplish the mission" }
    address { "#{Faker::Address.street_address}, #{Faker::Address.city}" }
    user
    photos {
      build_list(:photo, 3, search_terms: [Faker::Verb.ing_form])
    }
  end
end

现在,当您需要一项服务进行测试时,您可以要求提供。

service = FactoryBot.create(:service)

想要使用不同名称的服务?

service = FactoryBot.create(:service, name: "Universal Exports")

没有照片怎么办?

service = FactoryBot.create(:service, photos: [])

这只是 FactoryBot 功能的皮毛。

工厂比种子文件更灵活方便。

【讨论】:

  • 哇哦,谢谢大家的解释,真的很有帮助,超级有趣,我学到了很多!我会尝试在我的应用程序中使用factory,它看起来真的更简单,再次感谢 Schwern!
  • 吹毛求疵,但属性的作用更像def username ; @username ||= Faker::DDcComics.hero ; end,即它们memoize它们的值,因此您可以在整个工厂中重复使用它。
【解决方案2】:

aborted 消息中,必须有一个种子失败的特定行。可能是email。如果用户名是:“小丑”怎么办?电子邮件不能是“The Joker@gmail.com@”。

试试这个:

require 'faker'

puts "Creating 10 fake user heros..."
10.times do
  user = User.create!(
    username: Faker::DcComics.hero,
    password: "123456",
    email: "#{username..gsub(/\s+/, "").downcase}@gmail.com",
    bio: "Hi, I'm #{user.username}. #{Faker::Movie.quote}"
  )
  puts "#{user.username} created!"
end

【讨论】:

  • 感谢您的回答 Yshmarov,您是对的,它首先来自 email 我尝试了您的代码,但我仍然收到错误消息:NameError: undefined local variable or method `username' for main:Object。顺便说一句,你为什么使用split?使用这个不是更好吗:"The Joker".gsub(/\s+/, "").downcase
  • split 只是如何修改用户名的示例。
  • 其实您的gsub 方法非常好。 "The Joker".gsub(/\s+/, "").downcase => "thejoker"
  • 好吧有道理。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2020-10-24
  • 2019-06-19
  • 2011-02-28
  • 2021-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多