I have been trying to do this for ages and can seem to grasp it. hope someone can help ?
i have a 'message' model that has many through 'distribute' relationship to a 'contact_detail' model.
basically a message can have many contacts associated with it and a contact can have many messages.
I can get this to work and save it succesfully but i want also have a creater attribute on the 'distribute' model that i want to set to true for the creater of the message.
my form params are as follows :
{"message"=>{"message"=>"a great message ...",
"messagable_id"=>"58",
"title"=>"how are you ?",
"messagable_type"=>"MachineEnquiry",
"message_type_id"=>"1",
"contact_detail_ids"=>["2",
"2",
"11",
"7"]},
"commit"=>"Send message",
"datetime"=>""}
The 'distributes' model has a contact_detail_id' attribute and this is all saving but before save i want to set the create attribute along with a contact_detail_id.
I can so this after save but i want to validate that the creater has been set so i have to do this before save dont i ? and not sure how to do this.
Any ideas? hopefully someone can help ?
thanks in advance
rick
From the way you describe things, creator should the same for every distribution record associated with a particular message. Making much more sense to save add a new column and belongs_to relationship to Message.
class Message < ActiveRecord::Base
belongs_to :creator, :class_name => "User" # links creator to your User model
validates_presence_of :creator_id # ensures creator_id is not empty
...
end
Filling that field from the form is as simple as adding
<%= f.hidden_field :creator_id, current_user.id %>
If I'm wrong in assuming that distribution record for the same message will have the same creator, then you should look into accepts_nested_attributes_for to pass details to related models from a form.
Related
I have a very simple model
class Lifestyle < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :profiles
end
that has a has_and_belongs_to_many relationship with Profile
class Profile < ActiveRecord::Base
attr_accessible ...
belongs_to :occupation
has_and_belongs_to_many :lifestyles
accepts_nested_attributes_for :lifestyles
end
I want to use ActiveAdmin to edit the Profile object, but also assign Lifestyles to a profile. It should be similar to dealing with belongs_to :occupation, as this is sorted out automatically by ActiveAdmin to a dropbox with the options pre-filled with available occupations.
I've tried to use the has_many form builder method, but that only got me to show a form to type in the name of the Lifestyle and on submission, it returned an error.
f.object.lifestyles.build
f.has_many :lifestyles do |l|
l.input :name
end
Error I get:
Can't mass-assign protected attributes: lifestyles_attributes
The perfect way for me would be to build several checkboxes, one for each Lifestyle in the DB. Selected means that the lifestyle is connected to the profile, and unselected means to delete the relation.
I'm having great doubts that this is possible using ActiveAdmin and without having to create very complex logic to deal with this. I would really appreciate it if you'd give your opinion and advise me if I should go this way or approach it differently.
After some research, I am ready to answer my own question.
First, I have to say thanks to #Lichtamberg for suggesting the fix. However, that only complicates things (also regarding security, though not an issue in this case), and doesn't help me reach my ideal solution.
Digging more, I found out that this is a very common scenario in Rails, and it's actually explained in Ryan Bates' screencast no #17.
Therefore, in Rails, if you have a has_and_belongs_to_many (short form HABTM) association, you can easily set the ids of the other associated object through this method:
profile.lifestyle_ids = [1,2]
And this obviously works for forms if you've set the attr_accessible for lifestyle_ids:
class Profile < ActiveRecord::Base
attr_accessible :lifestyle_ids
end
In ActiveAdmin, because it uses Formtastic, you can use this method to output the correct fields (in this case checkboxes):
f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all
Also, I have simplified my form view so it's now merely this:
form do |f|
f.inputs # Include the default inputs
f.inputs "Lifestlyes" do # Make a panel that holds inputs for lifestyles
f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all # Use formtastic to output my collection of checkboxes
end
f.actions # Include the default actions
end
Ok, now this rendered perfectly in the view, but if I try and submit my changes, it gives me this database error:
PG::Error: ERROR: null value in column "created_at" violates not-null constraint
: INSERT INTO "lifestyles_profiles" ("profile_id", "lifestyle_id") VALUES (2, 1) RETURNING "id"
I found out that this is due to the fact that Rails 3.2 doesn't automatically update the timestamps for a HABTM association table (because they are extra attributes, and Rails only handles the _id attributes.
There are 2 solutions to fix this:
Either convert the association into a hm:t (has_many, :through =>)
Or remove the timestamps from the table
I'm going to go for 2) because I will never need the timestamps or any extra attributes.
I hope this helps other people having the same problems.
Edit: #cdesrosiers was closest to the solution but I already wrote this answer before I read his. Anyway, this is great nevertheless. I'm learning a lot.
Active Admin creates a thin DSL (Domain-Specific Language) over formtastic, so it's best to look at the formastic doc when you need form customization. There, you'll find that you might be able to use f.input :lifestyles, :as => :check_boxes to modify a has_and_belongs_to_many relationship.
I say "might" because I haven't tried this helper myself for your particular case, but these things have a tendency to just work automagically, so try it out.
Also, you probably won't need accepts_nested_attributes_for :lifestyles unless you actually want to modify the attributes of lifestyles from profiles, which I don't think is particularly useful when using active admin (just modify lifestyles directly).
Add
attr_accessible :lifestyles_attributes
f.e.:
class AccountsController < ApplicationController
attr_accessible :first_name, :last_name
end
In my Rails app Users can have many People which in turn can (but don't have to) belong to Organisations.
In short, this:
Users --< People >-- Organisations
Now, it would be nice to be able to create new organisations from within a people view somehow. It tried this:
class Person < ActiveRecord::Base
attr_accessible :name, :organisation_attributes
belongs_to :user
belongs_to :organisation
accepts_nested_attributes_for :organisation
end
But it's not working because Organisation is not a child of Person.
Is there another way to realise this?
Thanks for any help.
I can see that Person is actually a child of Organisation and its possible to make nested form for parent model also. And you are already using accepts_nested_attributes_for.
Im assuming that you want to show a Organisation form for a already saved person. Then
In your PeopleController#show method build the organisation
#person.build_organisation
And in people/show.html.erb
form_for(#person) do |f|
f.fields_for(:organisation) do |fo|
# show the fields of organisation here.
end
end
It should work.
Update:
I tried something similar and it worked :) Ive made a gist including the snippets.
Please follow the link https://gist.github.com/3841507 to see it working.
I have a Tournament model that needs 0, 1, or 2 contacts. I created a Contact model and set has_many :contacts on the Tournament and belongs_to :tournament on the Contact. The Tournament accepts_nested_attributes_for :contacts.
However, when I build the form for Tournament I don't quite see how I should do it. I'm thinking about having two fields_for :contacts but it feels messy. I also considered having two specific attributes on the Tournament model (something along the line of primary_contact and secondary_contact) but I'm not sure about how to do that.
Is there a "correct" way to do this? Any suggestions?
I'm on Rails 3.1 BTW.
fields_for :contacts is the right way to go.
Take advantage of the fact that, if tournament.contacts has multiple items, then a single fields_for :contacts will show multiple fieldsets.
Then take advantage of the fact that tournament.contacts.build will create an unsaved Contact and add it to the contacts collection. If you do this in the controller before showing the form then your fields_for will display this empty contact and use it correctly with its nested attributes
I think you shouldn't limit the contacts for 2 fields, because I think you should keep the flexibility of adding more contacts for a tournament later
I have done a small example (by using check boxes) between Project to users, you might be able to get idea
https://github.com/sameera207/HABTMsample
I'd suggest maybe adding a non-persistent contact_list attribute and then you could enter as many contacts as you need separated by commas into one field:
has_many :contacts
attr_accessor :contact_list
def contact_list=value
value.split(',').each do |email|
self.contacts.build(:email => email).save
end
end
def contact_list
self.contacts.join(',')
end
If you need to enter more information for each contact (not just a name, email, or phone number), then you would need more fields.
The following railscast may help you:
http://railscasts.com/episodes/196-nested-model-form-part-1
I'm new to this. I have a (sqlite3, but with ActiveRecord it doesn't matter) table called Messages and a model called Message. I want to find all messages in database that have user_id or reciever_id equal to the object user and his attribute id (for short user.id). I know it's probably just one simple line of code, but I wanna do it the right "rails" way and I don't have much experience with this.
I'm using Rails 3. Thanks for any help.
Cheers
I suspect that what you will want this relationship in many places in your code, and actually this represents a fundamental part of your application's design.
Conceptually a 'message' belongs to a 'sender' and also to a 'receiver'. In reverse, a 'user' has many messages that she has sent, and many messages that she has received.
in the Message model, add the following
belongs_to :user
belongs_to :receiver, :class_name => "User"
in the User model, add the following
has_many :messages
has_many :sent_or_received_messages, :class_name => "Message", :conditions => ["user_id = ? OR receiver_id = ?", id, id])
Now you can do this:
my_user.messages # all of the messages the user has sent
my_message.user # the user who sent the message
my_message.receiver # the user who received the message
my_user.sent_or_received_messages # all messages where the user was a sender or a receiver
I'm assuming that you mean a user has_many :messages....
# assuming you are looking for a particular user
Message.where(['user_id=? OR receiver_id=?', user.id, user.id])
First, I feel like I am approaching this the wrong way, but I'm not sure how else to do it. It's somewhat difficult to explain as well, so please bear with me.
I am using Javascript to allow users to add multiple text areas in the edit form, but these text areas are for a separate model. It basically allows the user to edit the information in two models rather than one. Here are the relationships:
class Incident < ActiveRecord::Base
has_many :incident_notes
belongs_to :user
end
class IncidentNote < ActiveRecord::Base
belongs_to :incident
belongs_to :user
end
class User < ActiveRecord::Base
has_many :incidents
has_many :incident_notes
end
When the user adds an "incident note", it should automatically identify the note with that particular user. I also want multiple users to be able to add notes to the same incident.
The problem I ran into is that when a user adds a new text area, rails isn't able to figure out that the new incident_note belongs_to the user. So it ends up creating the incident_note, but the user_id is nil. For example, in the logs I see the following insert statement when I edit the form and add a new note:
INSERT INTO "incident_notes" ("created_at", "updated_at", "user_id", "note", "incident_id") VALUES('2010-07-02 14:09:11', '2010-07-02 14:09:11', NULL, 'Another note', 8)
So what I've decided to try to do is manipulate the params for :incident in the update method. This way I can just add the user_id myself, however this seems un-rails-like, but I'm not sure how else to it.
When the form is submitted, the parameters look like this:
Parameters: {"commit"=>"Update", "action"=>"update", "_method"=>"put", "authenticity_token"=>"at/FBNxjq16Vrk8/iIscWn2IIdY1jtivzEQzSOn0I4k=", "id"=>"18", "customer_id"=>"4", "controller"=>"incidents", "incident"=>{"title"=>"agggh", "incident_status_id"=>"1", "incident_notes_attributes"=>{"1279033253229"=>{"_destroy"=>"", "note"=>"test"}, "0"=>{"id"=>"31", "_destroy"=>"", "note"=>"asdf"}}, "user_id"=>"2", "capc_id"=>"SDF01-071310-004"}}
So I thought I could edit this section:
"incident_notes_attributes"=>{"1279033253229"=>{"_destroy"=>"", "note"=>"test"}, "0"=>{"id"=>"31", "_destroy"=>"", "note"=>"another test"}}
As you can see, one of them does not have an id yet, which means it will be newly inserted into the table.
I want to add another attribute to the new item so it looks like this:
"incident_notes_attributes"=>{"1279033253229"=>{"_destroy"=>"", "note"=>"test", "user_id" => "2"}, "0"=>{"id"=>"31", "_destroy"=>"", "note"=>"another test"}}
But again this seems un-rails-like and I'm not sure how to get around it. Here is the update method for the Incident controller.
# PUT /incidents/1
# PUT /incidents/1.xml
def update
#incident = #customer.incidents.find(params[:id])
respond_to do |format|
if #incident.update_attributes(params[:incident])
# etc, etc
end
I thought I might be able to add something like the following:
params[:incident].incident_note_attributes.each do |inote_atts|
for att in inote_atts
if att.id == nil
att.user_id = current_user.id
end
end
end
But obviously incident_note_attributes is not a method. So I'm not sure what to do. How can I solve this problem?
Sorry for the wall of text. Any help is much appreciated!
I have a similar requirement and this is how I tackled it:
class Incident < ActiveRecord::Base
has_many :incident_notes
belongs_to :user
attr_accessor :new_incident_note
before_save :append_incident_note
protected
def append_incident_note
self.incident_notes.build(:note => self.new_incident_note) if !self.new_incident_note.blank?
end
end
and then in the form, you just use a standard rails form_for and use the new_incident_note as the attribute.
I chose this method because I knew it was just throwing data into the notes with minor data validations. If you have in depth validations, then I recommend using accepts_nested_attributes_for and fields_for. That is very well documented here.