Pytanie Jak zainicjować ActiveRecord z wartościami w Railsach?


W zwykłej java użyłbym:

public User(String name, String email) {
  this.name = name;
  this.email = f(email);
  this.admin = false;
}

Jednak nie mogłem znaleźć prostego standardowego sposobu robienia w szynach (3.2.3), z ActiveRecords.

1. Zastąp zainicjować

def initialize(attributes = {}, options = {})
  @name  = attributes[:name]
  @email = f(attributes[:email])
  @admin = false
end

ale może zostać pominięty podczas tworzenia zapisz z DB

2. używanie after_initialize oddzwonić

przez przesłanianie go:

def after_initialize(attributes = {}, options = {})
  ...
end

lub z makrem:

after_initialize : my_own_little_init
def my_own_little_init(attributes = {}, options = {})
  ...
end

ale może być niektóre problemy z deprecjacją.

Tam jest trochę inny spinki do mankietów w takim, ale mogą być nieaktualne.


Więc, jaka jest właściwa / standardowa metoda użycia?


24
2018-05-08 19:27


pochodzenie


Możesz już to zrobić bez potrzeby posiadania niestandardowego kodu: User.new(:name => 'Bon', :email => 'bob@example.com'). Czy chcesz go użyć w inny sposób? - Dylan Markow
masz rację. Chyba pytam o wartości domyślne, a nie wartości init, które są przekazywane podczas tworzenia - Asaf
lub wykonując pewne manipulacje na danym wejściu, podczas konstruowania - Asaf


Odpowiedzi:


Twoje domyślne wartości powinny być zdefiniowane w schemacie, gdy będą miały zastosowanie do WSZYSTKICH rekordów. Więc

def change
  creates_table :posts do |t|
    t.boolean :published, default: false
    t.string :title
    t.text :content
    t.references :author
    t.timestamps
  end
end

Tutaj każdy nowy Post będzie fałszywy do opublikowania. Jeśli chcesz uzyskać wartości domyślne na poziomie obiektu, najlepiej zastosować implementacje w stylu Factory:

User.build_admin(params)

def self.build_admin(params)
  user = User.new(params)
  user.admin = true
  user
end

16
2018-05-08 20:11



dzięki. Zasadniczo, jeśli chcę mieć np. znormalizowane wiadomości e-mail we wszystkich moich instancjach użytkowników (po utworzeniu), powinienem użyć def self.build_normalized(params) metoda fabryczna? która przechodzi pracę nad znalezieniem odpowiedniej metody tworzenia dla klienta mojego kodu. I nie obejmuje wszystkich ścieżek wykonania. Ponadto, czy istnieje narzędzie fabryczne do użycia w szynach? lub standardowy wzór? - Asaf
I ... jeśli umieścimy domyślne wartości w definicji schematu i jakiejkolwiek innej logice init w samym obiekcie, czy to nie oznacza, że ​​jeden problem dotyczy różnych miejsc kodu (i może poziomów abstrakcji)? - Asaf
@Asaf - Chciałbym użyć albo wartości początkowych db, albo wzorca fabrycznego, a nie obu. - Jesse Wolgamott


Według Rails Guides najlepszym sposobem na to jest z after_initialize. Ponieważ przy inicjalizacji musimy zadeklarować super, więc najlepiej jest użyć wywołania zwrotnego.


14
2018-04-07 13:50



Link jest wyłączony, jest after_create Nowa after_intitialize? - James McMahon
@JamesMcMahon nie, nadal mamy after_intitialize. Zaktualizowałem link do dokumentacji. - Mauro George
W ostatnich latach prawie całkowicie uniknąłem callbacków AR. Dla mnie oni sprawiają, że testowanie jest trudniejsze, a moje modele bardziej złożone. Chcę, żeby moje modele były naprawdę głupie. - bennick


Jednym z rozwiązań, które mi się podobają, są:

class User ...
   scope :admins, where(admin: true)

Następnie możesz zrobić oba: stworzyć nowego użytkownika w stanie administratora (tj admin==true) przez User.admins.new(...) a także pobrać wszystkich administratorów w ten sam sposób User.admins.

Możesz zrobić kilka zakresów i użyć ich jako szablonów do tworzenia / wyszukiwania. Możesz również użyć default_scope o tym samym znaczeniu, ale bez nazwy, ponieważ jest stosowana domyślnie.


5
2018-05-08 19:49



czy są jakieś problemy z używaniem default_scope za to? a co z innymi manipulacjami param w konstruktorze? - Asaf
Przydaje się, jeśli masz do czynienia z, powiedzmy, modelem komentarzy i chcesz zapisać skasowane komentarze dla FBI :) Wystarczy ustawić default_scope jak where(deleted: false) i nigdy nie spotkasz się z usuniętymi odpowiedziami, chyba że bezpośrednio zastąpisz swój zakres za pomocą, powiedzmy, Comment.where(deleted: true) lub przez unscoped metoda: Comment.unscoped. Są inne sposoby, ale ja to lubię :) - jdoe
Jestem trochę obeznany z zasięgiem, ale ze znalezieniem, a nie tworzeniem. jednak szukam sposobu na prawidłowe zainicjowanie moich obiektów, a nie "partycjonowanie" ich w zakresy ... - Asaf
tak czy inaczej, zacząłem od razu z nazwanymi teleskopami, aby znaleźć ... - Asaf
default_scopes są również bardzo przydatne do określania powiązanych rekordów, które muszą być ładowane wraz z bieżącym modelem. Nazywa się to żarliwym ładowaniem, jeśli nie jesteś zaznajomiony. Jeśli będziesz ładować powiązane modele w kółko i cyklicznie, możesz znacznie zmniejszyć trafienia DB w ten sposób: default_scope include: :tags. Ale to już inna historia. :) - jdoe


Tego ranka szukałem czegoś podobnego. Podczas gdy ustawienie domyślnej wartości w bazie danych oczywiście działa, wydaje się, że zerwanie z konwencją Railsów ma integralność danych (a zatem wartości domyślne?) Obsługiwane przez aplikację.

Natknąłem się na to stanowisko. Ponieważ możesz nie chcieć natychmiast zapisać rekordu w bazie danych, myślę, że najlepiej jest nadpisać metodę initialize wywołując write_attribute().

def initialize
  super
  write_attribute(name, "John Doe")
  write_attribute(email,  f(email))
  write_attribute(admin, false)
end

4
2017-10-13 08:13



z jakiegoś powodu powoduje to problemy, jeśli spróbujesz zrobić coś takiego jak Model.new (param1: arg1, param2: arg2), otrzymasz "ArgumentError: błędna liczba argumentów (1 za 0)", wygląda na to, że odpowiedź Mauro brzmi: lepsza jest lepsza, jeśli chcesz dynamicznie pisać atrybuty (np. losowe klucze) - concept47
Zgadzam się. Powinieneś raczej używać after_initialize. - clekstro
FYI Właściwie, after_create działało lepiej, gdy potrzebowałem, kiedy użyłem after_initialize, za każdym razem, gdy robiłem .find na modelu z losowym atrybutem, wartość zmieniałaby się, czego nie chciałem - concept47
To nie działa, ponieważ podpis metody jest niepoprawny, wystarczy zadeklarować opcjonalny parametr. - eduardo
Dodałem przykład opcjonalnych parametrów w następnej sugestii odpowiedzi. - Harry Fairbanks


To zadziała w szynach 4.

def initialize(params)
    super
    params[:name] = params[:name] + "xyz" 
    write_attribute(:name, params[:name]) 
    write_attribute(:some_other_field, "stuff")
    write_attribute(:email, params[:email])
    write_attribute(:admin, false)
end

3
2017-08-08 00:41