Chapter 3. Inserting, Updating, and Deleting: Everything changes

image with no caption

Change is a fact of life—especially for data. So far you’ve seen how to whip up a quick Rails application with scaffolding, and how to write your own code to publish data from a database. But what if you want users to be able to edit data your way? What if scaffolding doesn’t do what you want? In this chapter, you’ll learn how to insert, update, and delete data in exactly the way you want. And while you’re doing that, you’ll be taken deeper into how Rails really works and maybe even learn a little about security along the way.

People want to post new ads online

People love the MeBay site, but there’s a problem. Because MeBay was nervous about people having too much access to the data, sellers have to phone in details of their items to MeBay and wait while the system administrators create new ads for the sellers. As the number of people sending in ads has grown, so has the wait time. A lot of people are taking their business to other advertising sites now.

So MeBay has relented. After some discussion, they’ve decided that people should be allowed to post their own ads on the site using a page that looks like this:

image with no caption

You already know how to build an app that publishes data from the database

The ads only go one way in the current application. The ad records are read from the database by the model, which converts them into ad objects that are then sent to the view by the controller. It works like this:

image with no caption

Saving data works just the OPPOSITE of reading data

Saving data to the database is similar to publishing ads from the database, except it works the other way round. Instead of a page template to display an ad, you need a page template to submit an ad. Instead of a controller action method to send an ad to a page, you need a controller method to read data from a page and turn it into an object. And instead of the model reading a record and converting it into an object, you need the model to convert an object into a new record in the database.

You need a form to submit data and an action method to save it

You need a new page template to create the HTML form. Because it will be used to enter new ads, we’ll call this template new.html.erb.

The “New ad” page will need to appear at

http://mebay.com/ads/new

and the form will be submitted to:

http://mebay.com/ads/create

So we also need to create a new route in routes.rb.

Note

Remember, a route is what tells Rails which pieces of code to use to satisfy a request from a browser.

image with no caption

The top of your config/routes.rb file should now look like this:

image with no caption
image with no caption

There are close relationships between many parts of a Rails application.

After all, the model contains the data for the application, the view allows the user to access that data, and the controller provides the logical glue that connects everything together.

But is there some special relationship between a form and a model?

Are forms and objects related?

Apart from the generated id, the fields in the form match the attributes of an ad object.

image with no caption

At some point the application is going to have to transfer data between the form and model. The name field will match to a name attribute, the description field will match to a description attribute and so on.

What if the model creates objects with default values in the attributes? Should the code that generates the default values in the form duplicate the model code?

After all, when the data in the form is received by the controller, should the form treat the fields as individual values? Or should all of the field values be associated together, like the attributes of an object?

Could Rails make use of the relationship between form fields and a model object when creating a form?

Rails can create forms that are associated with model objects

Rails can use a model object to help create a form. That means two things:

  1. The values in the form fields will be set to the values stored in the attributes of the @ad object. This doesn’t make a lot of difference to the ad form because new ads are blank.

  2. The form fields will be given names that explicitly associate those fields with a model object.

So how can a name associate a field with an object? Let’s look at what it would mean for the ad form. This is the HTML that will be generated for a form that’s based on an Ad object:

image with no caption

Brain Barbell

Comparing the field names and their matching attributes, how do you think Rails will present the form data to the controller?

You have a few pages to think about this...

Field names

Object attributes

ad[name]

name

ad[description]

description

ad[price]

price

ad[seller_id]

seller_id

ad[email]

email

ad[img_url]

img_url

Do this!

Save this code to a file called app/views/ads/new.html.erb

Brain Power

Something’s gone awry with the form page. Look at the error message that was produced and see if you can work out what went wrong.

The @ad form object has not been created

The problem is caused by the @ad object. By default, a variable like @ad is set to a special value called nil, which means no value. If @ad is set to nil, instead of being set to an Ad object, it won’t have attributes like @ad.name, @ad.description, and so on.

If @ad doesn’t have attributes, does that cause a problem for the form?

You bet! The form is based on the @ad object, and the form accesses each of the object’s attributes to generate the initial values of the fields in the form. But as soon as the first attribute is called, nil is returned, and that causes an error.

So how can we avoid this problem?

image with no caption

The form object needs to be created before the form is displayed

When the page with the form is generated, the initial values of each of the form fields will come from one of the attributes of the associated object.

Can you think what the problem is with that?

The problem is that before the form can be generated, the new ad object needs to already exist. Of course, until the user completes the details of the ad, the object won’t be saved to the database—but even so, the object needs to be created before the page template is called.

image with no caption

Brain Power

Where in the application would you create the new ad object? If it’s a template or method, what would it be called?

The forms ad object will be created in the new action of the controller

  1. The forms ad object needs to be created before the new.html.erb page template is run.

    If you create the object in a controller method called new, this will be run before the new.html.erb template is called.

  1. So how do you create a new ad object?

    Ad.new returns a new object that you can assign to the @ad variable. The new object won’t be saved automatically to the database, but you only need it in memory where it can be used to generate the HTML form.

  1. Now that the object is assigned to the @ad variable in memory, new.html.erb will be able to use it to generate the HTML form within the /ads/new web page.

Each page template now has a matching controller method

The ads controller now has one method for each of the page template files. Rails will always call the controller method before generating a page from the page template.

It’s the combination of a controller method and a page template that make up an action. That’s why the action name appears in the name of the controller method and in name of the page template file.

An action is a controller method and a page template.

Do this!

image with no caption

Now you have the controller creating the new ad object before the new.html.erb page template runs, it’s time to see if the code works.

The form doesn’t send an object back, it sends DATA back

The form was generated using an Ad object. But what exactly does the form send back to the server when the form’s submitted?

Because the form uses HTTP, it can’t send the form object over the network. Instead it sends data.

But how is the data FORMATTED?

Think back to how routing works. When a request arrives, Rails sends the details of the request to the routing system, which inserts values into a data structure called params[...], with values for the action and the controller.

The params[...] data structure wasn’t created just for routing. It can also be used to store any data submitted to the application by a web form.

A form’s fields are recorded in the params[...] table along with the name :ad. Then, the value of the :ad variable is actually another table of values, a table that maps a field name to a field value:

Note

Actually the data structure is more properly called a Hash, or an Associative Array.

image with no caption

But what happens to this data when the controller receives it? And how can we actually USE this data?

Rails needs to convert the data into an object before it can be saved

Rails can only use objects to talk to the database, so before an ad can be saved to the database, you need to find some way to convert the form data into an Ad object.

The model can create objects from raw form data

How do you do that? Well, remember when you created a new blank Ad object to use with the form?

image with no caption

The Ad.new method can also be called with a set of hash table of values that will be used to initialize the attributes of the new Ad object. And the form data just happens to be contained in a hash object:

image with no caption

The controller create method, step-by-step

  1. Rails sends the form data to the controller using the params[...] hash.

  1. The controller can read the raw form data by looking at params[:ad]. It can then send this value to Ad.new(...) to construct a new Ad object.

  1. The Ad object that is returned by Ad.new(...) has attributes that match the values in the form fields.

The controller needs to save the record

The whole reason for converting the form data into an object was so you could save it.

How do you do that? With

@ad.save

When save is called on the model object, Rails inspects the attributes and generates a SQL insert statement to update the database:

image with no caption

With the save in place, the controller’s create method is complete:

def create
       @ad=Ad.new(params[:ad])
       @ad.save
end

Rails was complaining because it had no way of generating a RESPONSE to your request

HTTP works using pairs of requests and responses. For every request, there’s got to be a response.

When the controller’s create method completed, a new record was created successfully. Then Rails needed to generate a response page, so it looked for the page template that matched the current action. The current action was create so it looked for create.html.erb. But that template doesn’t exist!

image with no caption

So we need to write a create.html.erb page template. But what should the template have in it?

Brain Power

Is there a problem showing a link to the new ad in the create page?

If so, what is it?

Don’t create a new page, use an existing one

The users don’t want to see the intervening confirmation page generated by create.html.erb. They just want to go straight to their ad. So what do you do?

You could edit the create.html.erb page template so that it displays all of the new ad’s details, right? After all, the @ad variable is visible from the page template and it contains all of the details of the new ad.

But that would be a bad idea. Why? Because that would mean that the create.html.erb page would be exactly the same as the show.html.erb page template you use to display each ad.

image with no caption

Note

Think DRY: Don’t Repeat Yourself.

That’s duplication, and it would mean you had more code to maintain in the future. It would be much better if the create action in the controller can choose to display the show.html.erb page.

image with no caption

But how can a controller action display ANOTHER action’s page?

A controller method works together with a template to form an action. In all of the examples you’ve seen so far, both the controller method and the page template have been exclusively used for one action.

That’s why the controller methods and the page templates have included the action name somehow. When you were performing a show action, you used the show method in the ad controller and the show.html.erb page template.

And we still want to use a controller method and a page template to complete the action, but now we want to be able to choose which page template gets called with the controller.

A controller action is a controller method and a page template.

image with no caption

We need a way for the controller to say that the output is found at a different URL.

Redirects let the controller specify which view is displayed

A redirect is a special kind of response from the Rails application to the browser. A redirect tells the browser to go to a different URL for output. So even though the browser sent the form data to /ads/create, a redirect sends the browser to ads/17 (for example, if 17 is the id number of the new ad).

image with no caption

When a new ad is created the browser automatically jumps to the new page.

But what if an ad needs to be amended after it’s been posted?

Some users have made mistakes in their ad creations, and want to make changes to their ads. So they want more than just display and creation forms. Users now want to be able to edit their ads.

image with no caption

This means the system need to allow updates as well as inserts. Will that be difficult to do?

Updating an ad is just like creating one... only different

Even though the system can’t currently edit ads, will it be a lot of work to add an editing feature?

Think for a moment about the sequence the system goes through to insert an ad into the system:

  1. A new blank ad object gets created and this is used to generate the ad input form.

  2. The form is sent to the user, who updates the field values and submits the form back to the application

  3. The data fields are converted back into an Ad object, which is saved to the database.

  4. The user is forwarded to a page displaying the new ad.

    image with no caption

Suppose you want to change a page. What would you expect to see? Maybe a form with the ad details that you can re-submit and have the changes saved, which is kind of the same sequence you used to create ads. Let’s look at the change sequence in more detail...

Instead of creating an ad, you need to find one; instead of saving it, you need to update the ad

When someone edits an ad, they’ll use a form just like before. The user will change the details and the ad data will be saved. So just how similar is the change sequence to the creation sequence?

  1. An existing ad is read from the database, and this ad will be used to generate the change form.

  2. The form is sent to the user, who updates the field values and submits the form back to the application.

  3. The data fields are converted back into an Ad object, which is used to update the database.

  4. The user is forwarded to a page displaying the updated ad.

    image with no caption

You can see that the sequence between the two operations barely differs at all. In the creation sequence, a new ad object is created and saved to the database. In the change sequence an existing ad is read, updated, and saved to the database.

So you need to make sure you take the differences between the two operations into account. You’ll need to keep track of things like the ad id number in the change sequence.

Do you think you could add an edit feature to the application as it stands?

image with no caption

Brain Power

What could be done to the editing feature so that ads can be changed, but not vandalized?

Restricting access to a function

The application can now create and update data. But that means anyone who can create an ad can update all ads. And that’s a problem.

The MeBay owners want anyone to be able to create ads, but they don’t want everybody else to be able to change the ad once it’s been posted.

image with no caption

One way of preventing just anyone changing ads is to protect the update function with a username and password.

The guys at MeBay have decided that only system administrators will be able to change ads. So they want the new update functionality secured with an admin username and password.

Fortunately, Rails makes it really easy to drop security right in. We’re going to use a special kind of web security called HTTP Authentication. This is the kind of security that pops up a dialog box and asks for a username and password when someone tries to enter a secure area of a web site.

...but now old ads need to be deleted

The site is up and running, and everything’s going great, but after not too long there’s a problem: even after stuff gets sold, the ads stay there.

image with no caption

MeBay could use their own data entry systems to remove ads from the site, but they were so impressed by how simple the change function was, they’d like you to add a delete function to the website.

The feature will only be available to MeBay administrators, so they want the same security that was applied to the change functionality. Also, because there’s been a ton of spam as well as some spoof ads posted onto the site, they’d like to make the delete function easily available from the index page. That way they can remove inappropriate content with a single click.

Let’s look at what we need to do...

Doing it yourself gave you the power to do more than scaffolding

You can choose what functions are available.

You can add additional features like security.

And now you understand how to create code that inserts, updates and deletes data, you’ll be able to amend the code that scaffolding generates.

image with no caption

Tools for your Rails Toolbox

You’ve got Chapter 3 under your belt, and now you’ve added the ability to manually create applications that can insert, update and delete data.

Rails Tools

@ad.save saves a model objects

@ad.update_attributes updates a model object

redirect_to lets the controller send the browser to a different URL

http_authentication makes adding security a breeze

Ruby Tools

params[...] is a *hash*, which is like an array indexed by *name*

*nil* is a special default object that means “no value”

Inside Rails, calling methods on a nil object causes errors

“#{” and “}” can insert expressions into strings like “1 + 1 = #{1+1}”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
44.200.27.215