We have in our system a one to one relation.
Let's use the typical example of user -> address:
class User < ApplicationRecord
belongs_to :address
end
class Address < ApplicationRecord
has_one :user
end
In this scenario, the user has an address_id field, the address doesn't have any database field that references the user.
When I now go ahead and create a user and an address, everything is fine:
user = User.create(address: Address.create)
But when I create an additional address with the existing user I will end up with 2 addresses.
address.create(user: user)
Usually not a big deal, but for reporting purposes stuff can go wrong.
So my question: how do I make sure there is only one user associated with one address and vice versa?
To truly enforce it, you can define a unique index on the users.address_id column.
In general I'd suggest just not using Address.create though: if you use address.create_user, Active Record will do a better (though still not perfect, especially in the face of concurrent requests) job of keeping things under control for you.
(Creating a user from an address doesn't make much sense, of course... if you went down that path, I'd consider flipping the relationship to make the Address feel more subordinate to the User. The point is that manipulating it via the has_one side will get you more has_one-aware behaviour.)
I believe that the issue you're running into might have more to do with the controller than with model constraints.
Your User model could accept_nested_attributes_for :address.
Your new and edit actions could look something like this:
def new
#user = User.new
#user.build_address
end
def edit
#user = User.find(params[:id])
#user.build_address if #user.address.blank?
end
And your user form could have something like:
<%= fields_for :address do |address_form| %>
<%= address_form.text_field :street_line_one %>
<%= address_form.text_field :street_line_two %>
<%= address_form.text_field :city %>
<%= address_form.text_field :postal_code %>
<%= address_form.text_field :region_name %>
<% end %>
This should ensure that a new object is created when a new user is created, and should ensure that the existing address is updated when the user with an existing address is updated.
If that's still not working as expected, you'll have to add a bit more detail like what your controller and form look like to know exactly how to solve your issue.
Related
I have Event model and a show page which shows the #event page and the customer registration form.
Now, my client wants a form for a different kind of event, which would have customer fields as well but labeled not customer name but parent / carer's name (similar form for a customer) and he also wants a Add player button on the bottom which would allow the parent /carer to add max 3 players. So the buisness need is that a parent registers 'players'. It should be possible to add max 3 players. I am wondering how I could go about creating 4 customers in one form. To me it sounds a bit odd, to be honest. Is this even possible? Or should I introduce Parent model and a Player model and connect them with each other. So for specific kinds of events I would create a Parent/Carer and max 3 players.
<%= simple_form_for #customer do |f| %>
...
<% end %>
There is no complete details to give you an specific solution, but will guide you in the right direction to solve it with an example.
Let's say you have your Parent model and your players model, you want to add a parent with 3 players in the same form.
we define in your parent models that you can accept nested attributes for your players, so for example if you want to create a parent with some players, you can do something like Parent.create(params_with_players_too) and it will create the parent and create the players too, associated with that parent. of course, having in mind that the params comes in the correct way.
Class Parent < ActiveRecord::Base
has_many :books
accepts_nested_attributes_for :players
end
Class Player < ActiveRecord::Base
belongs_to :parent
end
after that, your form could be something like
<%= form_for #parent do |f| %>
<%= f.fields_for :players, [Player.new]*3 do |player| %>
<%= player.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
and in your controller, you have to permit the players params too, something like this
def create
#parent = Parent.new(parent_params)
if #parent.save
...
end
def parent_params
params.require(:parent).permit(:attribute1, players_attributes: [:id, :name])
end
of course, you will have to understand it and adapt it to your specific case, hope that this helps you.
I am trying to build a simple donation app in rails. In this application, patrons would give amounts of money to clients. Both the patron and the client share large amounts of the same functionality. They are both linked to a user and have a username. However, the client is also supposed to have a content_type and a content_list property. At first glance, my guess is that I want to have both my patron and client inherit from the account class. However, the client has additional functionality, which seems to preclude any STI-based implementation (though I will be the first to admit that my understanding of STI is shaky at best). As it stands, it seems to simply make more sense to write out two separate resources, but I would like to keep my code as DRY as humanly possible. Is there a simple way for me to create the behaviors I want through inheritance, or should I simply go with overlapping resources?
Here's an idea, as both of your user-types share the same basic functionalities.
As you said, make one make one unified User or Account model. Include the database fields customer (as boolean) and patron (also as boolean).
In the signup process, the user can then select if they're a patron or customer, as they would regardless.
Then inside your view, you can then call, if you use Devise for instance (which I personally think is great)
<% if current_user.patron == true %>
<!-- all relevant UI functionality for patrons -->
<% end %>
or if the User is a customer
<% if current_user.customer == true %>
<!-- all relevant UI functionality for customers -->
<% end %>
Or if you want to loop through a list of patrons or customers:
<% #user.where(:customer == true).each do |users| %>
<% #user.first_name %> <% #user.last_name %>
<% end %>
These are just basic examples, but it would do just fine for what you're trying to achieve.
P.S You could also create one migration called "account_type" as a
string and then with the help of radio_buttons in the signup process
store the account_type as a string value.
<%= f.radio_button :account_type, "customer" %>
<%= f.radio_button :account_type, "patron" %>
I actually think that would be better. Then you would split the views up
like this:
<% if current_user.account_type => "customer" %>
Show the list that only customer should have or see.
<% end %>
<% if current_user.account_type => "patron" %>
Show the list that only patron should have or see.
<% end %>
<% #user.where(:account_type => "customer").each do |users| %>
<% #user.first_name %> <% #user.last_name %>
<% end %>
Regarding my question: Will a patron ever be a client? Will a client ever be a patron?
If the answer is "yes", you should consider creating a separate entity/model for the resource they're relating to.
With model DonationCampaign (attributes client_id, among others):
has_one :client, :class_name => 'User'
has_many :patrons, :class_name => 'DonationCampaignPatron'
DonationCampaignPatron (attributes patron_id, among others):
belongs_to :patron, :class_name => 'User'
This allows you to keep the shared functionality of User and then extend functionality to specific campaigns, without having to make other models messy, and keeps things DRY.
If a DonationCampaign could then have multiple users (as administrators, per se), to extend, a DonationCampaignRole model would be required, donation_campaign_id, user_id, role
Say you're using CanCanCan,
if can? :manage, #campaign
if can? :contribute, #campaign
:contribute would have to be added to ability.rb and could simply be #campaign.client != User
Also, STI example (the good kind, at least):
class Animal < ActiveRecord::Base
def says
raise "Implement on subclass"
end
end
class Cat < Animal
# always has a type of 'Cat'
def says
"meow"
end
end
class Dog < Animal
# always has a type of 'Dog'
def says
"woof"
end
end
And there's only one table: animals
Edit: Based on your response: From what I can learn by briefly using Patreon they have a single User model (for authentication), and then they likely have a creator_page_id in the User column. They then have a separate model CreatorPage which has all the "Client" (in your terms) info associated with it.
Personally, I would stick with a single User model for authentication and then implement the aforementioned DonationCampaign/DonationCampaignPatreon business logic. It's the most extensible with the least amount of effort (both long and shot-term).
If, for whatever reason, a Client is restricted to contributing to other Clients once they are a Client, I would forego using STI on the User model.
I've been looking for a solution for a few days, in a Rails 4.1 app, so here is my question :
In a Rails app, I have my model User and Adress.
class User < ActiveRecord::Base
has_many :adresses
accepts_nested_attributes_for :adresses
class Adress < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
In my form, I make a form_tag for User, no problem.
But, how I can display to the final user, in a form, 2 adresses fields?
I use <%= f.fields_for :adress %> to display one, it's ok. But if I display two forms (so the user can enter 2 adresses) they have both the same name and the request post only keep one.
I read the doc at http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
but, still, I don't get it.
Is there a proper way to do it?
Thanks
I would suggest you to prepare two addresses in new action, add them to the use and then in the form reneder it with foreach.
I found this kind of solution here : http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Since you have multiple addresses I think foreach is way to go.
So, to help anyone who is noob in Rails and stuck the same way I was :
In your controller :
#user = User.new
#user.adresses = Adress.new, Adress.new
In your view, form :
<%= form_for #user do |f| %>
<%= f.fields_for :adresses do |a| %>
<%= wp.text_field :name %>
<% end %>
<% end %>
will print the name field for adress two times.
(thanks again to #NickCatib)
I have a user view and a rental view.
In my rental view im trying to show the current users name. I think I am pretty close but I can't work out this last bit.
This returns all of my users in a select list
<%= f.select :user_id, User.find(:all).collect {|t|
[t.user_name, t.id]} %>
This returns my current users ID
<%= f.number_field :user_id %>
So I thought I could do something like
<%= f.select :user_id, User.find(:user_id).collect {|t|
[t.user_name, t.id]} %>
Which I would want to only return the current user in a select list with their id as the value and their name in the list. If I do the above it tells me
Couldn't find User with id=user_id
So user_id is being passed as a literal string but I want to pass the user_id variable which should be somthing like 10. I don't know how to pass the user_id as a variable.
I'm fairly new to ROR, I might be going about this the completely wrong way. Any help is much appreciated.
I am assuming you have a rental object, for which you show the form, I assume it is an instance variable #rental, furthermore I assume that inside your Rental class there is the following relation
class Rental
belongs_to :user
end
Then you could just write the following:
f.select :user_id, [[#rental.user.user_name, #rental.user.id]]
Hope this helps.
On a related but less important note: it is really weird to have a column called user_name for a user: I would call that column just name, since it is part of a user anyway.
find() wants a variable, not a symbol. And :all probably isn't what you want. You should write a method in your controller like:
def user(u)
#user = User.find(u)
end
Then call the method in the view or whatever like (I don't know exactly what you're trying to do here):
<% user(current_user.id) %>
Then you'll have a #user object with which you may play, i.e.:
<%= f.select :user_id, [[#user.name, #user.id]] %>
I think you should be able to do:
<%= f.select :user_id, User.find(f.object.user_id).collect {|t| [t.user_name, t.id]} %>
This does seem a little odd to me though. I'd have thought either:
Your object has a proper association to the relevant user, in which case you should be able to do f.object.user.user_name and f.object.user.id.
If you genuinely want the currently logged in user, you should probably be asking your authentication framework/code for the reference. E.g. if you were using Devise, it would be current_user.
As an aside, I don't really understand why you want a select list just containing the current user - is that definitely what you're trying to achieve, or have I misunderstood?
I am trying to build a form for my users where they can enter up to 3 addresses for themselves. After an address is created, it can be marked as inactive and won't count toward the 3. I am using accepts_nested_attributes_for :addresses in my User model and I've tried a few things in my form, but I can't get fields_for to show existing addresses and new addresses when the existing addresses are scoped.
These are a few things I've tried:
In the controller:
(3 - #user.addresses.active.count).times { #user.addresses.build }
This gives me all of the existing, active addresses but no new ones:
<%= f.fields_for :addresses, #user.addresses.active do |address| %>
<%= address.text_field :line_1 %>
<% end %>
This gives me existing and new addresses, but it includes the inactive ones:
<%= f.fields_for :addresses do |address| %>
<%= address.text_field :line_1 %>
<% end %>
Is there a way to combine these to get new AND scoped records?
From what I have found it isn't possible to explicitly change the scope and show fields for new records by changing the parameters passed to fields_for.
But what ended up solving my problem is the fact that when passing a symbol to fields_for (for example: fields_for :addresses) the default scope of the model is going to be used. So I set default_scope where(:status => 'active') on my address model and now fields_for will only show the fields for active addresses and the fields for new addresses.
I also needed to sort these addresses by the date they were created. This was possible by changing the default sort on the has_many :addresses relation in the user model. So while it doesn't seem there is a way to explicitly state what scopes to use in the parameters for fields_for, there are ways around it. If anyone can explain how to do this I will gladly mark that answer as correct.