Short explanation:
I seek architectural advice and help in implementing multiple Devise models in a single app.
More detailed explanation:
My application needs to perform the following behavior:
There are 3 types of users (Partner, Attendee, Speaker) which have some common fields and some unique ones (also, the fields might have different permissions, i.e. Attendee must have a username whereas the Speaker might have it but they don't have to necessarily fill this field in). And moreover, the different user models must have different associations with other tables in the db.
The users need to be able to log in through single log-in form, but the sign-up forms should be different.
So, my first thought was that I should divide the users by roles using Pundit or declarative_authorization or something, but the users don't really have different roles (i.e. permissions) in the app, they rather have different behavior, they might see different content and stuff, so I continued thinking.
My second though was implementing STI and after reading several articles about it, I tried to do that in code.
I generated a Devise User model by doing rails g devise User and after that ran rails g model Attendee and the same for other two users.
Then, I inherited my models from User:
class Attendee < User
end
My User migration looks like this:
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :type
# Devise stuff ...
..................
t.timestamps null: false
end
And other migrations are like this:
create_table :attendees do |t|
t.string :username
t.string :company_name
t.boolean :subscription
t.timestamps null: false
end
Now I realize it was wrong to create separate tables.
I had to put all the possible fields into the User table, is that correct?
Because now when I try to create any new Attendee or Speaker or Partner in rails console, all of these three models have the exact same fields, those the User model has.
But if I add all the possible fields in the User model, how would I perform validations on field presence?
I've read quite a few articles and questions here on SO, but still can't really wrap my head around how to implement all that.
Anyway, is that a correct way to do what I need?
Could anybody explain me in a detailed way how I should implement this kind of behavior and functionality from start to finish, and how should I work with the models after having implemented them?
PS: here's the history of my migrations and the whole github repo
Update
Remembered another issue that stopped my from doing just role separation:
How should I sign up the different users with different sign-up forms? Different routes? I cannot make the user choose their role from the combobox.
You can create conditional validation rules based on the role, but the first place you need to address this is in the new/edit User form, only showing the allowed fields dynamically based on the role:
class User < ActiveRecord::Base
validates :company, presence: true, if: :is_company?
def is_company
# check for the role
self.role == 'company'
end
end
UPDATE: You can pass an extra parameter to the same registration form and use that to differentiate the type of form you display. That's the nicest way. You can also create separate methods in the UserController -> def register_user, def register_company, def register_xxxx
Related
I have a parent User model, and each user has_one Contact which hold's the user's contact info.
One of the contact fields is the email. For various legacy reasons and reasons outside the context of this question, I'd love to be able to call #email directly as if it were a property of User
In other words I'd like to do this -
user.email
user.email = "foo#example.com"
Instead of this -
user.contact.email
user.contact.email = "foo#example.com"
I laid out my models as follows, overriding the child's getter/setter from the parent User -
class User < ActiveRecord::Base
has_one :contact
def email
contact.email
end
def email=(value)
contact.email = value
end
end
class Contact < ActiveRecord::Base
belongs_to :user
end
You may have already noticed the problem with the setter - when the User is saved, it doesn't save the child model.
What's the most robust to work around this?
Thanks!
Ok, found the solution for anyone curious - auto-saving
ActiveRecord::AutosaveAssociation is a module that helps with exactly this - saving child associations properly when the parent is saved.
Just need to include an autosave: true. Example -
has_one :contact, autosave: true
It also takes care of destroying records marked for deletion, and a few other fancy things.
I also did a quick check, and it smartly doesn't hit the DB unless it really needs to. That is, if you update the parent only it doesn't bother saving the child association with an other SQL UPDATE statement.
Check out the documentation here.
Each Widget has an associated User. Users have a String attribute called email which stores the email address associated with the User.
The User model has a unique id attribute, as per Rails conventions, but also ensures that email addresses are unique. Therefore, User.where(email: foo#example.com) is guaranteed to return at most one result.
In a form to create a Widget, I would like users to be able to specify an email address, rather than an ID. Both id and email are unique keys for Users, but email addresses are more convenient for people.
What's the recommended way for the Widget controller to accept an association reference that names the associate by a key other than id? When using id, if the widget[user_id] parameter stores the ID for a User, mass-assignment (in create or update) stores the correct User in the Widget. Is there any built-in Rails support for making something like widget[user_email] work, or should I add code in the controller to recognize that parameter, look up the User, and associate the User with the Widget?
Here's the code:
class Widget < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
end
class WidgetsController
load_and_authorize_resource
def create
#widget.save
end
def widget_params
params.require(:widget).permit(...)
end
end
I've considered:
Modifying the Widget model to add a setter for "user_email", looking up the User there, and setting "user_id" appropriately.
Modifying WidgetsController#widget_params to notice the presence of "widget[user_email]", look up the User, and replace it with "widget[user_id]".
I'm leaning towards the second option.
Is there a better way?
I have a users and venues model. venues belongs_to users and users can have_many venues.
I would like a user to be able to set a default venue. I am new to programming/development and am keen to learn best practices.
So, should a venue have a boolean 'default_venue' column which only allows one row to be 'true' per user. Or should I be creating a default_venue model? And if so, any tips on what/how this would look like?
I would just add a default_venue_id to the Users table. Then write a small method on the User model.
# User.rb
...blah blah validations
...blah blah other code
def default_venue
Venue.where(id: default_venue_id)
end
Then going forward in your Controllers (or Rails Console), this would be valid code:
#Returns default venue for User
User.find(:id).default_venue
I would like to show only one nested form for the one-to-many relationship between user and appointments. A user can only create one appointment and the form should only show fields for one appointment regardless of how many appointments he has.
I've tried the :limit option
class User
has_many :appointments, dependent: :destroy
accepts_nested_attributes_for :appointments, limit: 1
end
I also tried limiting the number of appointments built in the controller
class UsersController
def edit
#user = current_user
1.times { #appointment = #user.appointments.build }
end
end
I always end up with a nested form for each appointment the user has plus an additional nested form. I know I could change the relationship to one-to-one but this is for legacy code and its likely that the relationship will be one-to-many in the future.
Is there a way that I can only show one nested form regardless of how many appointments a user has?
I have a rails 4 application whereby I've set up a User model. I want the User to have a has_many association with a user profile model, but here's the catch:
My user profile model needs to be polymorphic - the
User model can have multiple (different) user profiles associated
with it (e.g. ProfileTypeA, ProfileTypeB, ProfileTypeC, etc.)
I want my User model to have one association, say, user_profiles, that
would return the all the user's user profiles associated with it.
I believe I'm on the right track (or am I?), but how would this be accomplished using the rails generator? The part that is most confusing to me, is how to do bullet #2 above.
P.S. I took a look at STI's, but it seems to me that my User model would have to have a hard-association with each user profile type model, which I don't like because it would change the User model with each new user profile type I add to the data model.
You sound on right track,try below
#The polymorphic models
class User
has_many :user_profiles, as: :profileable
end
class UserProfile
belongs_to :profileable, polymorphic: true
end
The Migrations below
#migrations
class CreateUserProfiles < ActiveRecord::Migration
def change
create_table :user_profiles do |t|
t.integer :profileable_id
t.string :profileable_type
t.timestamps null: false
end
add_index :user_profiles, :profileable_id
end
end