Back-end: I have a model (User) that has_many of another model (ContactPreference).
Front-end: An interface allowing the user to reorder, add, and delete contact preferences for a particular user.
I'd like to let the user commit all their changes all at once with a single form submit. The way I'm doing this now is with allows_nested_attributes_for :contact_preferences in the User model, and naively POSTing the attributes of the edited preferences list. It works just fine except for a glaring bug: If a user deletes a contact preference, the ID simply isn't sent, and the preference doesn't get deleted from the DB.
allows_nested_attributes_for has support for deleting objects from the collection, but it requires the client to keep track of what IDs were deleted and pass a '_destroy' => 1 parameter. This is messy logic that I'd rather avoid; I just want objects deleted unless they are explicitly included in the parameters. allows_nested_attributes_for doesn't support this behavior as far as I can tell, so I'm looking to implement my own solution.
What's the most efficient (in terms of database access) way to do this kind of update? Do I delete everything and rebuild the list from scratch? Do I load the association and pick out objects that aren't explicitly included? Perhaps there's some clever ActiveRecord magic I can use?
My personal feeling is that doing this using the :destroy => 1 flag set a lot less messy than the alternative. The alternative would be loading the association on the server, comparing the incoming parameters, figuring out which records are missing, then deleting the missing ones and updating the remaining ones. That's a lot of extra logic, DB operations, and worst of all, you'll have to hand-rework the accepts_nested_attributes_for which is a non-trivial feat.
HTML give you a little trick/hack to accomplish this without JS. Add a checkbox to each record with name :destroy. Use the high-level form helpers, e.g. check_box, not check_box_tag (which requires a lot of things to get right manually), or a higher level form helper such as the simple_form gem.
If the flag is not checked, then HTML won't submit anything, and the record stays. If the flag is checked, HTML will submit the :destroy flag, and it will be deleted with the built-in server-side mechanisms out of the box.
You didn't say much about your front-end code; it sounds like you have a bunch of JS on there. You probably hide the record when the user "removes" it, you can simply add the destroy flag programmatically in that case, if you don't want to use the check box method above. This will be a lot simpler and less error prone than trying to second-guess the backend behavior.
Related
With the ActsAsTaggableOn gem I would like to retrieve the most used tags used for posts by a certain user. Currently I have a user.rb model and a post.rb model which belongs_to the user. What I can do is this:
ActsAsTaggableOn::Tag.most_used
Which does show me the most used tags used overall. However, I would like to filter that down to only show me the most used tags by the current_user. I was thinking of something like:
ActsAsTaggableOn::Tag.most_used.joins(:posts).where(user_id: current_user.id)
which does not work since there is no connection established and therefore i cant join the models. How can I access the most used tags in the posts by the current_user?
ActsAsTaggableOn::Tag.joins(:taggable).where(taggable: current_user.posts).distinct.most_used(5)
After tinkering around with a lot of queries I got the solution now which might be a bit tricky.
Basically what ActsAsTaggableOn does is just assigning tags to the respective model. The tags however are in no way really related to anything else. What is possible is to assign tag ownership.
What I do now could be considered as bad practice: First the Post is assigned a tag which is stored in the tag_list, then I am going to trigger an after save action inside the posts.rb which grabs the last tag of the post object (only one tag can be assigned in my app) and assigns it to the user via
#user.tag(#post, with: "Some tag", on: :posts)
It would have been way easier to just code the whole thing myself and not rely on a gem, but its okay for now.
ive been digging through quite a few questions on here about restful relations but havent quite found what im after.
consider this scenario
GET /api/users(?include=custom_fields) - returns an array of users, optionally including relations as nested properties
GET /api/users/1(?include=custom_fields) - returns a single user object
POST /api/users - creates a new user (if the request includes an array of "custom_fields" these are created and linked)
PUT /api/users/1 - replaces user entity with supplied payload
PATCH /api/users/1 - updates the user properties provided
DELETE /api/users/1 - deletes
GET /api/users/1/custom_fields - gets all users custom_fields
PUT /api/users/1/custom_fields - deletes all existing custom_fields and creates ones provided
PATCH /api/users/1/custom_fields - appends if not exists, or creates new custom fields for this user
DELETE /api/users/1/custom_fields - deletes all custom fields for user
DELETE /api/users/1/custom_fields/{id} - deletes custom field for use by id.
this all makes sense to me and is working as expected, however im now implementing a "user edit" screen in my admin area, this shows the user object AND the custom fields.
right now the only RESTFUL way i can see to save this form is to:
PATCH to /api/users/{id}
to save the user. when thats done,
PUT to /api/users/{id}/custom_fields
to update the custom fields.
not ideal but would work, however going forward i know for sure i will have other related resources like user roles, emails, etc.
this doesnt change the situation it just means alot more endpoints.
something about this smells to me. to simply save a user im having to make requests to at least two endpoints.
how would you suggest best to handle this without the two different requests?
The simplest solution would be to just include all those (custom fields, roles, etc.) into the User representation, you already seem to be doing that anyway on GET and POST. Just extend that to PUT and PATCH too.
If you already support some media-type for the PATCH method on Users, then you could conceivably extend that to define adding/removing custom fields, roles, whatever you need.
For example, I recently added 'address' fields to my user model. They are being validated by presence. So now, when I went to update a different attribute, email for example, I can't save the user because it has invalid fields (the address is still blank).
Do you just have to migrate the data with blank strings or something?
What is the Rails way to handle this?
One way to handle this is to populate the new field in the migration which creates it. That way, you're ensuring that everything in the database is valid according to the model. The guide has some pointers on doing this - there are a couple of gotchas.
Another way is by specifying that the validation only runs on creation, not on updates, with the :on option. That might look like:
validates :address, :presence => true, :on => :create
That way, any new user has to have the address set, but you're still able to edit other attributes on existing users.
I suggest you do the migration every time you add new fields and put validations on them.
It would keep the data integrity and consistency.
If you really want to do a trick, adding conditions on validation, putting "validate: false" conditionally when you do save, adding blank value for address fields when you modify an existing records all are tricks you can use.
I also suggest that when you add address, create a address model and share it among other models which need it, instead of only adding fields into the models. That would make your validation more flexible.
I use acts-as-taggable-on https://github.com/mbleigh/acts-as-taggable-on for tagging user's post, I want to find one user's all post' all tags and list them, Is there a high performance method ?
You need to add acts_as_tagger to the User model
refer to https://github.com/mbleigh/acts-as-taggable-on#tag-ownership
then you can use the methods provided. If you're starting from a blank db, this should work but may need to run through some re-assigning loop to have the tagger association work.
Assuming your data is correct, you will then be able to do:
#some_user.owned_taggings
#some_user.owned_tags
Hope this helps
I trying to prevent url hacking, I passing an id to the url that the forms need, it works fine but if the user changes that value on the url it will send values to the wrong table.
<%= link_to '+ New Event',
{:controller =>'events', :action =>
'new', :company_id => company.id} %>
On the php world I used to encrypt that id ...how can I do this on rails3 or is there a better way ??
needless to say I sort of new to rails and I know a little bit of php
any help or suggestions will be greatly appreciated.
Even though this is an older question, it's a very worthwhile question. It is absolutely worthwhile to conceal the ID in the URL for, among other things, prevention of information disclosure.
For example, an application has a robust security model allowing users to only view resources to which they have rights. However, why should a user be able to look at the value of the ID in the URL and use it to deduce how many resources there are or, as the original questioner suggests, start trying to poke around with forced browsing.
The solution to this in rails turns out to be pretty simple. What I find works best is overriding to_param in the models, usually via a module in the lib directory and a before_filter in the application controller that decrypts the IDs.
For a walkthrough, have a look at this: http://www.youtube.com/watch?v=UW_s9ejrCsI
Rather than trying to encrypt or hide your company.id value, ask yourself what exactly it is that you want to prevent users from doing.
If you just want to prevent users from creating events associated with non-existant companies (by setting the id to a really high value for instance), then a simple
validates_presence_of :company
On the Event model would be fine.
If you only want users to be able to create events associated with companies that they work for, or have access for in some way, then you should create custom validations to verify that.
F