How rails form instance works? - ruby

I have these two methods, show and create.
def show
#article = Article.find(params[:id])
#comment = Comment.new
#comment.article_id = #article.id
end
def create
#comment = Comment.new(comment_params)
#comment.article_id = params[:article_id]
#comment.save
redirect_to article_path(#comment.article)
end
Show method displays my comment form.
In the show method why do we create a new instance of Comment and also associate the comment
instance to an article id.
Create method actually handles the submission of form.
In the create method, again i am creating a new comment object and again associating the comment's article_id.
My whole questing is why were repeating these things?. Why do I have associate my comment form with article_id when I am displaying it and again I am repeating the steps while submitting the form too.

This repetition could be avoided if you keep those resources nested and build the form as:
<%= form_for(#article, #article.comments.build) do |f| %>
Hope this helps! :)

The reason that you initialize a comment both times is because the user's browser only sees the html form - it doesn't have a concept of a Comment - and because each request to a Rails application is independent - nothing is persisted in the application between requests:
When the user requests the show page for an Article, the request is handled by the application something like this:
The controller creates a new Comment object (in memory).
The form_for helpers in the view build a form from that Comment.
The html for the show page is sent to the user's browser.
At this point the application has done everything it needs to serve this request, so the temporary Comment object is deleted.
When the user submits the form, the values that were entered are sent to the application in the comment_params and the application handles this request like this:
The controller creates a new Comment object (again in memory), but initializes it with the data that the user sent through in comment_params.
The controller saves the Comment - this stores the Comment in the database so it can be loaded later.
The controller redirects back to the show page.
Saving to the database is the main way that the application can persist things between requests - objects in memory only exist while the request is being processed.

Related

rails different form_for parameters for different tasks?

I have the following two form_for() that I am confused about.
# form for users
form_for(#user) do |f|
# form for sessions
form_for(:sessions, url: login_path) do |f|
I understand that the first one is simply go through all the users and input the form. I am not sure why when you create a new session, the parameters for form_for is listed as such. Why is there a login path?
Michael Hartl explained it as "in the case of sessions we need to indicate the name of the resource and the corresponding URL", but that doesnt' really explain anything to me.
any enlightenment would be nice!
Passing :url to form_for method is optional when dealing with a model object. when using it to create a new object:
# long-style:
form_for(#user, url: users_path)
# same thing, short-style (record identification gets used)
form_for(#user)
In the short-style version a concept called Record Indentification is used, basically rails figures out if the record is new by asking record.new_record? It also selects the correct path to submit to based on the class of the object, in this case #user.class
Same principle applies when using form_for when updating an existing object. In this case the record.new_record? returns false, and rails figures out it must submit the form to the update action of the controller.
In cases when the path that the form must submit to cannot be figured out by rails using the above mechanism, then the url must be provided explicitly. This also applies when defining Singular Resources such as resource :geocoder. when creating forms for singular routes, the URL must be specified:
form_for #geocoder, url: geocoder_path do |f|
In the first case, the form_for helper inspects the #user variable to see its state. The User might be a new record (in which case the form points to /users with POST*) or an existing record (users/:id with PATCH). It can also provide pre-filled inputs based on the variable's state.
The second form is used when there's no need for an instance of a model (or maybe the model doesn't even exists) and the form_for helper is used just for convenience. The url parameter is explicitly set because by default the action would point to the current URL and you want it to point to login_path.
*Technically they are both POST on the browser side, Rails will distinguish between them later.

New comment w/ajax - owner_id not in request parameters until page is refreshed

I have a comments form which attaches to the "revision" of a "section". If no revision exists, an empty one is created. The add comment form is loaded with ajax. The issue, is that if several comments are added without refreshing the page, several empty revisions are created. This causes havoc in certain edge cases.
I request a new add comment form with ajax every time the add comment button is hit. However, Rails thinks there's no new information and serves 304 not modified. There is new information - the comment form on the second request should have a populated "owner_id" which is the id of the empty revision created by the first comment.
I'm positive the owner_id population works as refreshing the whole page fixes the issue. I used to think this was a server cache issue but looking at the parameters of the request, owner_id is missing until the page is refreshed.
Update: I added expire_now() to the new action on comments controller and it's now serving a 200 response but the contents still have no owner_id.
Another update: Looking in the console, the parameters of the request don't include the owner_id. Refreshing the page then requesting the form suddenly includes the owner_id in the parameters.
I'm still not sure about the details on this, but an ajax request is different from a page request in a number of ways. For example, controller code is not updated when just requesting a view with ajax. In my case, there is also difference of not having owner_id as a populated parameter for ajax views.
In the new action on the comments controller, I had to manually query the section to see if there was a revision, and if so, set the owner_id with the revision id.
def new
# precaution against creating multiple empty revisions if user adds multiple comments without refreshing page
if params[:owner_id].nil?
revision = #section.send(params[:snip_type]).first
unless revision.nil?
#comment.owner_id = revision.id
end
end
end

django, return to previous page after form POST submit

In my web page I have a form that is being filled with data after some ajax requests. For example when a user chooses an item from the list, a simple ajax request is sent to the database that the item has been selected (but not confirmed, yet). Then the list on the web page reloads using a simpe ajax request (just the list, not the whole page) to fetch the new item list.
I think this is more or less a classic cart implementation.
However, when the user presses submit (classic form POST submit, not ajax POST for some reasons concerning the implementation) to confirm the whole list, I would like to return to the current page. (Current page varies) Is this possible? I am using django.
Thanks.
You can supply a next GET parameter when submitting the form, similar to django.contrib.auth's login() method:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.login:
<form action="/url/to/post/form/?next={{ some_variable }}">
where the variable can simply be the current URL (taken from the request) or a generated URL. In the view processing the form, simply check for a next parameter and redirect appropriately:
from django.shortcuts import redirect
if 'next' in request.GET:
return redirect(request.GET['next'])
You may be able to use the Post/Redirect/Get Design pattern (PRG). For more general information about Post/Redirect/Get please see the following: http://en.wikipedia.org/wiki/Post/Redirect/Get There are some nice process flow diagrams there.
A generic example of a view implementing PRG might look like the following:
# urls.py
urlpatterns = patterns('',
url(r'^/$', views.my_view, name='named_url'),
)
# forms.py
class MyForm(forms.Form):
pass # the form
# views.py
def my_view(request, template_name='template.html'):
""" Example PostRedirectGet
This example uses a request context, but isn't
necessary for the PRG
"""
if request.POST:
form = MyForm(request.POST)
if form.is_valid():
try:
form.save()
# on success, the request is redirected as a GET
return HttpResponseRedirect(reverse('named_url'))
except:
pass # handling can go here
else:
form = MyForm()
return render_to_response(template_name, {
'form':form
}, context_instance=RequestContext(request))
If you need to do something more interesting with the GET, reverse can take args and kwargs. Manipulate the view params, url_pattern, and reverse call to display the results you would like to see.
One additional note is that you don't have to redirect to the same view (as this example does). It could be any named view that you would like to redirect the user to.
current page is a very vague term but i am assuming you want the page that referred you to the form page, this is normally (not always) stored in the HTTP_REFERRER header of the request itself. You could try to fetch that from the request and do a redirect.

Sinatra - How to best move variable/parameter between pages

I have a register page with the usual email,name,password ..which is validated in the server's submitted route/page. if it fails then I redirect back but I want to fill the values back in the register page..I can put the register form parameters in the session but it will stay there...is there a page memory(a smaller scope than session) just like session which will be just for the next page and then gone/ which is the best way to implement this.
Thanks
Why don't you just render the registration page from the POST route like this:
post '/register' do
#registration_data = params[:stuff] # store all your registration data
if info_validates # everything validates
redirect './user_home'
else # something fails validation
haml :register # or erb or whatever your template engine is
end
end
Then in your view, have it fill in #registration_data if it exists.
Also, you can clear session data with session.clear.
Ajax validation would be much easier. You just register an onclick event to your form submit button that makes a call to a page that returns a json status code with the error information or 200 for OK. If 200, then submit.

Django Forms - Processing GET Requests

We have an existing Django form that accepts GET requests to allow users to bookmark their resulting query parameters. The form contains many fields, most of which are required. The form uses semi-standard boilerplate for handling the request, substituting GET for POST:
if request.method == 'GET':
form = myForm(request.GET)
if form.isValid()
# Gather fields together into query.
else
form = myForm()
The problem is that the first time the form is loaded, there's nothing in the GET request to populate the required fields with, so most of the form lights up with 'missing field' errors.
Setting initial values doesn't work; apparently, the non-existent values in the GET request override them.
How can we avoid this? I'm pretty certain we're simply not processing things correctly, but I can't find an example of a form that handles GET requests. We want errors to show up if the user hits the "Submit" button while fields are blank or otherwise invalid, but don't want these errors showing up when the form is initially displayed.
The positional argument to the forms.Form subclass informs Django that you intend to process a form rather than just display a blank/default form. Your if request.method == 'GET' isn't making the distinction that you want because regular old web requests by typing a URL in a web browser or clicking a link are also GET requests, so request.method is equal to GET either way.
You need some differentiating mechanism such that you can tell the difference between a form display and a form process.
Ideas:
If your processing is done via. AJAX, you could use if request.is_ajax() as your conditional.
Alternatively, you could include a GET token that signifies that the request is processing. Under this example, first you'd need something in your form:
<input type="hidden" name="action" value="process_form" />
And then you can look for that value in your view:
if 'action' in request.GET and request.GET['action'] == 'process_form':
form = myForm(request.GET)
if form.is_valid():
# form processing code
else:
form = myForm()
I'll also give you the standard, boilerplate point that it's generally preferable not to use GET for form processing if you can help it (precisely because you run into difficulties like this since you're using an anomalous pattern), but if you have a use case where you really need it, then you really need it. You know your needs better than I do. :-)
If your clean page load doesn't have any non form GET params, you can differentiate between a clean page load and a form submit in your view. Instead of the usual
form = YourForm()
if request.POST:
you can do
if request.GET.items():
form = YourForm(request.GET)
if form.is_valid():
...
else:
form = YourForm()
If your clean page load could have other params (eg email link tracking params) you'll need to use the QueryDict methods to test if any of your form params are in the request.
request.GET is and empty dictionary when you first load a clean form. Once you have submitted the form, request.GET will be populated with your fields data, even if the fields contain only empty data.
My first question is this, which I posted as comment:
Why not just use request.POST and the standard way of processing form data?
After considering everything here, perhaps what you are looking for is a way of processing data in your query string to populate a form. You can do that without using request.GET as your form.data.
In my own views, I take advantage of a utility function I created to add initial data to the form from request.GET, but I am not going to share that function here. Here's the signature, though. initial_dict is typically request.GET. model_forms is either a single ModelForm or a list of ModelForm.
def process_initial_data(model_forms, initial_dict):
Nevertheless, I am able to process the form through the standard practice of using request.POST when the form is POSTed. And I don't have to pass around all kinds of information in the URL query string or modify it with JavaScript as the user enters information.

Resources