Conversation
29c4227 to
6f106a9
Compare
app/models/contact.rb
Outdated
| has_and_belongs_to_many :users, join_table: :users_contacts | ||
|
|
||
| validates :name, :email, presence: true, length: { maximum: 255 } | ||
| end |
There was a problem hiding this comment.
Модель для новых контактов. Мапится на существующюю таблицу users.
Пробросил связь с User через has_and_belongs_to_many.
There was a problem hiding this comment.
я бы все же завел промежуточную модель Membership как принадлежность контакта какому-то юзеру и обратно тогда можно было бы has_many :through использовать http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
А в дальнейшем в эту модель Membership можно было добавить поля с информацией о связи, например принял ли contact вашу подписку или нет.
There was a problem hiding this comment.
Модель Membership завел
app/models/user.rb
Outdated
| :trackable, :validatable | ||
|
|
||
| has_many :newsletters | ||
| has_and_belongs_to_many :contacts, join_table: :users_contacts |
There was a problem hiding this comment.
Теперь можно делать так:
User.first.contacts.create(name: 'name', email: 'email@gm.com')
Но поле email в users уникальное и кинет ошибку если другой юзер захочет добавить такой же контакт. Как быть ? Сделать email не уникальным ?
Я все равно склоняюсь к тому, чтобы отдельно хранить users и contacts :)
There was a problem hiding this comment.
Разделять схожие данные пока смысла нет, они очень похожи.
Имея has_many :contacts, through: :membership мы точно так же можем делать
User.first.memberships << User.find_or_create_by(email: params[:email]) и тд. тогда e-mail будет уникальный всегда, только связи между User - Membership - User будут меняться
mpakus
left a comment
There was a problem hiding this comment.
Отлично, глянь только идею про Membership для has_many :through, именование связующих таблиц и надо небольшой тестик сделать на связи, уникальность добавления и e-mail
| @@ -0,0 +1,8 @@ | |||
| class CreateUsersContacts < ActiveRecord::Migration[5.0] | |||
| def change | |||
| create_table :users_contacts, id: false do |t| | |||
There was a problem hiding this comment.
Тут глобальная проблема эти join_table нужно называть в порядке отсортированных моделей соединяемых, тогда не надо будет явно указывать join_table contacts_users
3.3.2 Creating Join Tables for has_and_belongs_to_many Associations
If you create a has_and_belongs_to_many association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the :join_table option, Active Record creates the name by using the lexical book of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
http://guides.rubyonrails.org/association_basics.html#updating-the-schema
There was a problem hiding this comment.
точно, не обратил внимания
app/models/contact.rb
Outdated
| has_and_belongs_to_many :users, join_table: :users_contacts | ||
|
|
||
| validates :name, :email, presence: true, length: { maximum: 255 } | ||
| end |
There was a problem hiding this comment.
я бы все же завел промежуточную модель Membership как принадлежность контакта какому-то юзеру и обратно тогда можно было бы has_many :through использовать http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
А в дальнейшем в эту модель Membership можно было добавить поля с информацией о связи, например принял ли contact вашу подписку или нет.
app/models/user.rb
Outdated
| :trackable, :validatable | ||
|
|
||
| has_many :newsletters | ||
| has_and_belongs_to_many :contacts, join_table: :users_contacts |
There was a problem hiding this comment.
Разделять схожие данные пока смысла нет, они очень похожи.
Имея has_many :contacts, through: :membership мы точно так же можем делать
User.first.memberships << User.find_or_create_by(email: params[:email]) и тд. тогда e-mail будет уникальный всегда, только связи между User - Membership - User будут меняться
db/schema.rb
Outdated
| t.integer "contact_id" | ||
| t.index ["contact_id"], name: "index_users_contacts_on_contact_id", using: :btree | ||
| t.index ["user_id"], name: "index_users_contacts_on_user_id", using: :btree | ||
| end |
There was a problem hiding this comment.
можно еще уникальность поставить на [:user_id, :contact_id] индекса
8226af2 to
64af274
Compare
| gem 'sextant', group: [:development] | ||
| gem 'slim-rails' | ||
| gem 'spring', group: [:development] | ||
| gem 'spring-commands-rspec', group: [:test] |
There was a problem hiding this comment.
чтобы из Vim можно было запускать тесты
64af274 to
9a28379
Compare
|
|
||
| def edit; end | ||
|
|
||
| def destroy; end |
There was a problem hiding this comment.
Будем удалять только саму связь из memberships
| end | ||
| end | ||
|
|
||
| def edit; end |
There was a problem hiding this comment.
Редактирование нужно, пока не совсем понятны такие моменты:
- если редактировать email. Я добавил контакт user@gmail.com. После этого, кто-то другой тоже хотел добавить этот же email, но так как он уже в БД, будет сохранена только связь. Если отредактирую email, то это коснется и других, кто тоже добавил этот контакт
- тоже самое и с именем. Если я поменяю имя контакта, то другие пользователь это заметят :)
There was a problem hiding this comment.
Ничего страшного, agile и подразумевается, что запустил некую фичу за какой-то определенный период (спринт), пошел отзыв, что-то не понравилось переделал, это всегда можно сделать например хранить имена в Membership
| else | ||
| @contacts = contacts | ||
| @contact = contact | ||
| render :index |
There was a problem hiding this comment.
не очень нравится, как сделал сохранение. Тут две операции: добавляем контакт, создаем связь. Это две разные модели, у каждой есть своя валидация.
Хотел бы показатьmembership.errors на форме, как например, contacts.errors, чтобы все ошибки показывались в одном месте и одном формате, но не знаю как это сделать. Поэтому, membership.errors показываю как alert.
Как правильно нужно сделать ?
Кстати, еще вопрос: в базе есть контактname: tom, email: tom@gmail.com. Я добавляю контакт name: thomas, email: tom@gmail.com. Т.е. я перезаписываю существующее имя ?
There was a problem hiding this comment.
- для такого можно использовать сервис классы + транзакция, но я думаю попозже покажу как это можно рефакторить, в целом стоит посмотреть на методы save! которые вызывают exception и как обернуть такие атомарные операции в 1 транзакцию можно например тут http://vaidehijoshi.github.io/blog/2015/08/18/safer-sql-using-activerecord-transactions/
- да, имя перезапишется на новое
There was a problem hiding this comment.
@mpakus понял, сервис попробую сделать.
Я думал про транзакции, а потом решил, что даже если юзер сохранился, а связь нет, это не страшно, так как при следующем добавлении этого юзера(а он уже будет в БД) сохраниться только связь.
Ну а вообще, конечно, согласен, что это должно быть одной транзакцией.
mpakus
left a comment
There was a problem hiding this comment.
Все гуд, по мелочи только (если хочешь транзакции можно позднее добавить) и как тебе идея name перенести в связь Membership?
| else | ||
| @contacts = contacts | ||
| @contact = contact | ||
| render :index |
There was a problem hiding this comment.
- для такого можно использовать сервис классы + транзакция, но я думаю попозже покажу как это можно рефакторить, в целом стоит посмотреть на методы save! которые вызывают exception и как обернуть такие атомарные операции в 1 транзакцию можно например тут http://vaidehijoshi.github.io/blog/2015/08/18/safer-sql-using-activerecord-transactions/
- да, имя перезапишется на новое
| end | ||
| end | ||
|
|
||
| def edit; end |
There was a problem hiding this comment.
Ничего страшного, agile и подразумевается, что запустил некую фичу за какой-то определенный период (спринт), пошел отзыв, что-то не понравилось переделал, это всегда можно сделать например хранить имена в Membership
app/models/contact.rb
Outdated
| @@ -0,0 +1,9 @@ | |||
| # frozen_string_literal: true | |||
| class Contact < ApplicationRecord | |||
| self.table_name = 'users' | |||
There was a problem hiding this comment.
такие штуки только путают людей мне кажется пусть тогда или таблица будет contacts или модель User
There was a problem hiding this comment.
@mpakus А как тогда сделать user.contacts и contact.users ?
Ну и модель User требует password. Если так, тогда можно любую строку присваивать.
Ну это ладно, а как сделать такую связь, которую выше описал, без доп. модели Contact ?
db/schema.rb
Outdated
| ActiveRecord::Schema.define(version: 20160925080729) do | ||
|
|
||
| create_table "memberships", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t| | ||
| t.integer "user_id" |
There was a problem hiding this comment.
кстати да, подумай может name действительно удобнее будет для каждой связки в memberships хранить
There was a problem hiding this comment.
@mpakus согласен, это решит проблему перезатирания имен. Может, мы так дойдем что и email будем разделять. Но пока да, нет необходимости для этого. Редактирование email не будет. К нему могут привязаны много контактов. Если что, контакт можно удалить и добавить заново с правильным email.
- create Contact model - create association between users and contacts - create Contacts controller. Add contact - update spec tests
| t.integer "contact_id" | ||
| t.datetime "created_at", null: false | ||
| t.datetime "updated_at", null: false | ||
| t.string "name" |
app/models/contact.rb
Outdated
|
|
||
| validates :name, :email, presence: true, length: { maximum: 255 } | ||
|
|
||
| delegate :name, to: :memberships |
There was a problem hiding this comment.
@mpakus Вот тут застрял. Как получить имя контакта из memberships ?
Чтобы было так: current_user.contacts.first.name и Contact.find(:id).name. Сейчас вернет имя из таблицы users.
И опять сомнения по поводу контактов в таблице users.
user.contacts - это гуд. contact.users - вообще не гуд. В самом приложении мы так делать никогда не будем. В админке или консоли для дебага, возможно. Но не в приложении. Контакт не должен знать к каким еще пользователям он относится.
Т.е. как я думаю связь должна быть такая: contact.user, а не contact.users
Мы не можем написать contact.users, нужно делать что-то вроде этого:
Contact.first.users.where(users: { id: current_user.id}).first.name, чтобы получить контакт текущего юзера, а не любого другого, имеющего такой же контакт.
И не понятно, как получить имя из memberships, когда пишем .name
There was a problem hiding this comment.
@mpakus давай может сделаем users и contacts(name, email, user_id) отдельно ? :)
Да, будут повторения имейлов, но как-то это все прозрачней обслуживается.
Я бы уже давно контакты, а так застрял сильно.
|
Я внес корректировки, погляди на модели User - Membership, тестики тоже добавил. Суть что на самом деле User может являться как и subscriber - лицо подписавшим кого-то, так и subscribed - тем кого подписали (following-follower). Вот у Hartla можно подробнее почитать https://www.railstutorial.org/book/following_users |
No description provided.