# Iterare dati dentro un modello

**URL:** <https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993>\
**Category:** IT\
**Created:** [August 13, 2015, 6:37pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993 "2015-08-13T18:37:06Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rafa\_F](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/958977/32.png) [@Rafa\_F](https://www.ruby-forum.com/u/Rafa_F)\
**Post date:** [August 13, 2015, 6:37pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/1 "2015-08-13T18:37:06Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 1:02pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/2 "2015-08-14T13:02:10Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 4:11pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/3 "2015-08-14T16:11:01Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 5:16pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/4 "2015-08-14T17:16:53Z")

</div>

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 E. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 5:31pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/5 "2015-08-14T17:31:45Z")

</div>

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

1. 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…

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 6:12pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/6 "2015-08-14T18:12:11Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 5:51pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/7 "2015-08-14T17:51:56Z")

</div>

On 14 August 2015 at 17:31, Simon E. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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] [Active Record Associations — Ruby on Rails Guides](http://guides.rubyonrails.org/association_basics.html)

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 6:15pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/8 "2015-08-14T18:15:19Z")

</div>

Una passeggiate, una buona dormita e un bel bagno sono le muse migliori  
degli sviluppatori 😉

2015-08-14 18:12 GMT+02:00 Simon E. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

---

<div class="post-metadata">

**Author:** ![fordp](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/f/4bbf92/32.png) [@fordp](https://www.ruby-forum.com/u/fordp)\
**Post date:** [August 14, 2015, 6:24pm UTC](https://www.ruby-forum.com/t/iterare-dati-dentro-un-modello/242993/9 "2015-08-14T18:24:16Z")

</div>

Ahahahaha Maurizio… ed è proprio così…🙂
