Forum: Italian Ruby user group ITERARE DATI DENTRO UN MODELLO

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
027f88d17068dde230d0261d70bea262?d=identicon&s=25 Simon Eric (noumedem)
on 2015-08-13 18:37
Hello everybody,

I have 2 tables users and videos. I create another table (video_users)
to link them with easiness of rails.

On the view when user 1 clic play button  to view  video 1, user_id and
video_id are load automatically on table videos_users.

When user clic again, the same thing append  on new record.

Example:

This is what happend on database:

user_id video_id
   1      1
   1      1

How can I have something like this?

user_id video_id number_view
   1      1          2


Model User:

class User < ActiveRecord::Base
   has_many :video_users
  has_many :videos, through: :video_users
end

Model Video:

class Video < ActiveRecord::Base
  has_many :video_users
  has_many :users, through: :video_users
end

Model VideoUser

class VideoUser < ActiveRecord::Base
  belongs_to :video
  belongs_to :user
end

Migration video_users:

class CreateVideoUsers < ActiveRecord::Migration
  def change
    create_table :video_users do |t|
      t.references :video, index: true
      t.references :user, index: true

      t.timestamps
    end
  end
end



When i tried to create column number_view, i have some error:
SQLite3::SQLException: no such table: videos_users: ALTER TABLE
"videos_users" ADD "number_view" integer DEFAULT 1


Thank you very much
321db48bf4bdf48da05e781325aed20a?d=identicon&s=25 Maurizio De magnis (olistik)
on 2015-08-14 13:02
(Received via mailing list)
Hi Simon,

to achieve your goal you could do this:

    video_user = VideoUser.find_or_create_by(user_id: params[:user_id],
video_id: params[:video_id])
    video_user.number_view ||= 0
    video_user.number_view += 1
    video_user.save

It looks like your join table is named "video_users" so within the
migration in which you try to create the column "number_view" you should
specify that name instead of "videos_users".
On top of that I suggest you to specify the default value of 0 for
"number_view":

      t.integer :number_view, default: 0

so that you can simply increment that field and avoid the line:

    video_user.number_view ||= 0

As a side note, it would be better to name join tables with the
convention
of putting the names alphabetically ordered and in the plural form. This
means that you could've named your join table "users_videos" because
"users" comes before "videos" and each of them is specified in the
plural
form.
027f88d17068dde230d0261d70bea262?d=identicon&s=25 Simon Eric (noumedem)
on 2015-08-14 16:11
Ciao Maurizio,

grazie mille per il tuo aiuto.

Ho fatto tutto quello che hai detto. Grazie di cuore.

Funziona!

Però il record viene sempre creato e non va bene cosi.

La situazione è questa nel db:

L'user clicca per la prima volta:

user_id video_id number_view
   1      1           1

Lo stesso user clicca sullo stesso video per la seconda volta:

user_id video_id number_view
   1      1          2      --> Effetto del secondo click
   1      1          0      --> Rails lo fa automaticamente secondo i
legami tra le tavole User, Video e VideoUser

Quindi viene sempre creato un nuovo record con il valore di default di
number_view.

Questo è il codice:

 def iterate_number_view(user_id, video_id)
    video_user = VideoUser.find_or_create_by(user_id: user_id, video_id:
video_id)
    video_user.number_view ||= 0
    video_user.number_view += 1
    video_user.save
  end

Questo è il refactoring della migration di VideoUser

class CreateVideoUsers < ActiveRecord::Migration
  def change
    create_table :video_users do |t|
      t.references :video, index: true
      t.references :user, index: true
      t.integer :number_view, default: 0
      t.timestamps
    end
  end
end


Come bloccare la second azione cioè quello che fa rails in modo
automatico?

Ho provato di rompere i legami cioè rendere ogni tavola indipendente
però  non mi sta bene perché i legami mi aiutano a fare altre azioni.


Grazie per l'aiuto
321db48bf4bdf48da05e781325aed20a?d=identicon&s=25 Maurizio De magnis (olistik)
on 2015-08-14 17:16
(Received via mailing list)
Ciao Simon (giusto? :-))

il fatto che `number_view` venga aggiornato ad ogni successiva azione mi
fa
pensare che il metodo `#iterate_number_view` sia corretto.
1) Puoi condividere il metodo del controller chiamato in seguito
all'azione
dell'utente?
2) Giusto per essere sicuri, hai per caso aggiunto delle callback
`after_create` o `after_save` ai model `User` e `Video`?


2015-08-14 16:11 GMT+02:00 Simon Eric <kkenzo2007@yahoo.fr>:
027f88d17068dde230d0261d70bea262?d=identicon&s=25 Simon Eric (noumedem)
on 2015-08-14 17:31
1) Puoi condividere il metodo del controller chiamato in seguito
all'azione
dell'utente?

In pratica il codice è cosi
nel video_controller, questo è il metodo che viene chiamato quando
l'utente clicca:


def play
   current_user.try :watch_video, @video
    VideoUser.iterate_number_view(current_user.id, @video.id)
    respond_to do |format|
      format.js
    end
end

poi viene chiamato questo:

def self.iterate_number_view(user_id, video_id)
    video_user = self.find_or_create_by(user_id: user_id, video_id:
video_id)
    video_user.number_view ||= 0
    video_user.number_view += 1
    video_user.save
 end

current_user è l'user corrente, try è un metodo per l'eccezione

Ecco il metodo watch_video che è scritto nel model user

def watch_video video
    videos << video
 end


2) Giusto per essere sicuri, hai per caso aggiunto delle callback
`after_create` o `after_save` ai model `User` e `Video`?

No no

Grazie Maurizio.

Da giorni che cerco di risolvere questo problema -( -(... Ma...
321db48bf4bdf48da05e781325aed20a?d=identicon&s=25 Maurizio De magnis (olistik)
on 2015-08-14 17:51
(Received via mailing list)
On 14 August 2015 at 17:31, Simon Eric <kkenzo2007@yahoo.fr> wrote:

>    current_user.try :watch_video, @video
> video_id)
>     video_user.number_view ||= 0
>     video_user.number_view += 1
>     video_user.save
>  end
>
> current_user è l'user corrente, try è un metodo per l'eccezione
>
> Ecco il metodo watch_video che è scritto nel model user
>
>
Ecco il colpevole ;-)


> def watch_video video
>     videos << video
>  end
>

`User` e `Video` sono legati da una relazione `has_many :through` (vedi
"La
Guida" [0]), che condivide molti dei concetti (e metodi) delle relazioni
`has_and_belongs_to_many`. È una relazione N:M tra due entità, `User` e
`Video`, e come tale richiede la presenza della tabella d'appoggio
(`video_users`, rappresentata esplicitamente in ActiveRecord tramite la
classe `VideoUser`).
Il motivo per cui non hai potuto optare per la relazione
`has_and_belongs_to_many` e hai dovuto invece scegliere `has_many
:through`
è perché nel caso di `has_and_belongs_to_many` non puoi aggiungere
attributi specifici al legame tra le due entità (come la colonna
`number_view` nel tuo caso).

Ok, tutto molto bello fino a qui.

Come dicevo prima, questo tipo di relazioni condivide alcuni metodi
d'utilità che ti permettono di gestire i record delle tabelle a livello
Ruby.
All'interno del contesto di una variabile d'istanza di `User`, puoi
avere
accesso a tutti i record di tipo `Video` collegati ad essa tramite il
metodo `videos`, il quale ti fornisce l'astrazione di un normale `Array`
Ruby (più o meno, dato che non è proprio un oggetto della classe Array,
quanto invece un oggetto della classe
ActiveRecord_Associations_CollectionProxy) e ogni volta che aggiungi un
elemento a `videos` (tramite `videos << oggetto`), vengono eseguite
delle
callback ActiveRecord che aggiornano lo stato del database, creando un
corrispondente record nella join table `video_users`.

Quindi, dato che con `iterate_number_view` te già stai creando e/o
aggiornando i record necessari, non hai più bisogno del metodo
`watch_video` :-)

[0] http://guides.rubyonrails.org/association_basics.html
027f88d17068dde230d0261d70bea262?d=identicon&s=25 Simon Eric (noumedem)
on 2015-08-14 18:12
Appunto bel,

Sono un somaro. Quando fatto un giro fuori che mi sono reso conto che
stavo sbagliare.
Ho semplicemente cancellato quella linea là.

Tutto gira come desideravo

Grazie per tutto Maurizio.

Ciao e buon week-end
321db48bf4bdf48da05e781325aed20a?d=identicon&s=25 Maurizio De magnis (olistik)
on 2015-08-14 18:15
(Received via mailing list)
Una passeggiate, una buona dormita e un bel bagno sono le muse migliori
degli sviluppatori ;-)

2015-08-14 18:12 GMT+02:00 Simon Eric <kkenzo2007@yahoo.fr>:
027f88d17068dde230d0261d70bea262?d=identicon&s=25 Simon Eric (noumedem)
on 2015-08-14 18:24
Ahahahaha Maurizio.... ed è proprio così...:)
This topic is locked and can not be replied to.