当前位置:Linux教程 - Linux - 网络数据库5日教程(5)

网络数据库5日教程(5)



         原作者:<a href=mailto:rdick@pobox.com>Richard Dice</a>


    --------------------------------------------------------------------------------

    网络数据库教程-[第5天]


    一 必不可少的CGI.pm
    二 数据库转义序列
    三 在Here-Document字符串内嵌入引用表达式
    四 嵌入子程序
    五 用SQL设置表单- selection.iphtml
    六 用CGI.pm - receive.iphtml处理表单结果

    --------------------------------------------------------------------------------

    一、 必不可少的CGI.pm

    在过去的几天里我们已经谈了许多重要的事项。当然谈得都不是太深入。今天我将用一个实例比较细致地阐述一下过去几天所学的概念。而且几天还会涉及一些新的问题。

    在各种有关Perl CGI编程的讨论中常常会提到Lincoln Stein的CGI.pm 。总体来说,CGI.pm 所用的编程理念和我在本教程中所倡导的完全相反。

    从一开始,我就在试图帮助你们建立互动、动态生成的网页。这也是嵌入式HTML编程的初衷,而且它也能使网站的编码和组织更简便快捷。

    而CGI.pm的观点是将网络编程分离出来,它给你一种感觉仿佛你是在用CGI.pm搭建一个网页,例:

    #!/usr/bin/perl

    use CGI;

    $query = new CGI;



    print $query->header(),

    $query->start_html(-title=>\"Made with CGI.pm\"),

    \"This is what I mean by \",

    $query->b(\"abstracted\"),

    \".\",

    $query->end_html();

    exit 0;
    该程序的执行结果是一个简单的HTTP头,一个起始HTML块(包括一个<TITLE>),几行文字,以及标准的HTML结束块。注意所有这些都是用CGI的$query 对象以及与其相关的方法编制的:

    Content-type: text/html <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\"> <HTML><HEAD><TITLE>Made with CGI.pm</TITLE> </HEAD><BODY>This is what I mean by <B>abstracted</B>.</BODY></HTML>

    我总是尽量避免使用这种编程方法,但是我也不得不承认这种方法有时候
    用起来很顺手。


    CGI.pm的优点不在于它的抽象性,而在于它所提供的工具。CGI.pm有一个非常强大的cookie处理器以及表单参数解码方法。

    我们都了解表单,它是HTML,而且它具有很多用途。

    View the page source to see how I put this one together.

    作为一个网络程序员,表单是从用户处收集的只要方法。当一个用户点击递交(Submit)按钮时,浏览器将用户输入的信息打包编码然后发送给你的CGI程序进行处理。而CGI程序的职责就是要解包,解码和使用表单中的内容。

    这个过程很烦琐,为什么不让CGI.pm帮你处理这些事情呢?表单参数可以用param方法提取:

     

    #!/usr/bin/perl

    #

    # BAR.cgi is a simple Perl CGI program using CGI.pm.

    #

    # A Web page with a form containing the FOO textarea

    # is meant to submit to BAR.cgi (pay attention to

    # the \"param\" method).

    #



    use CGI;

    $query = new CGI;



    print $query->header(),

    $query->start_html(-title=>\"Test of the param method\"),

    \"The value of the FOO parameter is: \",

    $query->param(\"FOO\"),

    $query->end_html();

    exit 0;
    这就是用CGI.pm解决这个问题的方法。在以后的某些ePerl代码中我还将用到CGI.pm,但只是为了利用其param方法的优势。

    二、 数据库转义序列

    在第3天的教程中我用做了一个唱片数据库,其中有一张唱片叫做\"We Can\"t Dance\",这一项不能作为字符串利用mysql程序插入到数据库中-因为MySQL将符号\"作为字符串的限位符。

    变通方法是通过加入反斜杠将该限位符“转义”:

    mysql> insert into albums(title,artist,released)

    -> values(\"We Can\\\"t Dance\",\"Genesis\",\"1991-01-01\");
    当然在为了数据库上下文中处理这些细节比较令人头疼。

    当你想在数据库中插入项目时,通常会直接使用表单参数。你的ePerl代码可能如下:

    $foo = $query->param(\"foo\");

    $foo =~ s/\"/\\\\\"/g; # this Perl command will substitute \" with \\\"



    $SQL = <<\"EOT\";

    insert into my_table(my_column)

    values (\"$foo\")

    EOT
    这种设置方法不好,因为:

    MySQL用反斜杠\"转义\"字符,但其他的某些数据库程序则可能使用不同的转义序列,所以用反斜杠硬性“转义”会影响程序的可移植性另外,该转义符在此处只转义了单引号 \" -还有其他的字符也需要转义。

    需要转义的其他字符在不同的数据库中会有所区别。

    你不需要记忆文献中所述的细节。用$dbh->quote记忆可以帮你实现上述目的。

    $foo = $dbh->quote($query->param(\"foo\"));
    $query->param(\"foo\")将返回表单文字区输入的foo的值,并且$dbh->quote将根据和你的数据库相应的DBD转义必要的字符。

    $dbh->quote还在这个字符串外添加引号,这样就省得你在键盘输入引号了。

    三、 在Here-Document字符串内嵌入引用表达式

    here-document字符串的语法如下:

    $string = <<\"HERE_DOCUMENT\";

    You can type all sorts of stuff in here....

    You can also $interpolate variables right into your h-d string.

    The here-document string will quit when it runs into the label

    given at its outset.

    HERE_DOCUMENT
    here_document非常方便,特别是在编制传递给$dbh以生成客户端$cursor光标查询的 $SQL字符串时。

    看一下我们如何在上面的$dbh->quote例子生成$SQL。 我必须生成一个中介变量$foo插入实际生成$SQL的here-document字符串。但这样也不是很理想。我们可以做得更好。

    问题是,当$dbh->quote返回一个字符串标量值时,它不是字符串标量值,它是一个函数(实际上是一个方法),这个问题可以在here-documents中变通解决。例如,下面的代码就不能执行:

    $SQL = <<\"EOT\";

    insert into my_table(my_column)

    values ($dbh->quote($query->param(\"foo\")))

    EOT
    我在here-document做一些变通 (变通方法用加重体字显示)

    $SQL = <<\"EOT\";

    insert into my_table(my_column)

    values (${ \\($dbh->quote($query->param(\"foo\"))) })

    EOT
    \\ 对$dbh->quote所提供的值作了一个引用。${ ... }取消该引用。语法看起来不好看,但是,据我所知,从来也没有人认为Perl 是一种漂亮的语言。

    四、 嵌入子程序

    尽管ePerl可以让你在HTML中任意嵌入Perl,但有时候在你的.iphtml文件中
    使用子程序能使你的程序显得简洁明了。

    例:


    <HTML>
    <HEAD><TITLE>Embedding a Subroutine</TITLE></HEAD>
    <BODY>

    <P>
    Here\"s an HTML calendar for the current month:
    <B><?=${ \\(`cal| head -1`) }!></B>

    <P>
    <?=${ \\( calendar_table() ) }!>//

    </BODY>
    </HTML>
    <?

    sub calendar_table {

    #
    # I make copious use of the Unix \"cal\" command here.
    # This won\"t work on DOS-derivative machines.
    #

    my @cal = `cal`; # fill the cal array with the
    output of a shelled cal command
    my $return = \"\";

    shift @cal; # junk the first line... it\"s not needed

    $return .= \"<TABLE BORDER>\\n\";
    $return .= \"<TR>\";
    # I dedicate the following line to my fellow Perl junkies
    everywhere
    $return .= join(\"\", map { s/\\W//g; \"<TH>$_</TH>\" }
    unpack(\"A2A3A3A3A3A3A3\", shift @cal));
    $return .= \"</TR>\\n\";

    foreach ( @cal ) {
    $return .= \"<TR>\\n\";
    $return .= join(\"\", map { s/\\W//g; \"<TD>$_</TD>\" }
    unpack(\"A2A3A3A3A3A3A3\",$_));
    $return .= \"</TR>\\n\";
    }

    $return .= \"</TABLE>\\n\"; # the value of this line is
    returned

    }

    !>//
    这里是该程序执行的结果(如下)。 注意如果子程序非常大的话可能会给内存造成麻烦(因为$return的体积会很大),但是当我们遇到这个问题时我们会用相应的办法解决。

    Su Mo Tu We Th Fr Sa
      1 2 3 4 5 6
    7 8 9 10 11 12 13
    14 15 16 17 18 19 20
    21 22 23 24 25 26 27
    28 29 30        
                 

     

    五、 用SQL设置表单 - selection.iphtml

    这里是我所用到的例子,它包括两个程序selection.iphtml和receive.iphtml。

    首先调用selection.iphtml - 它将提供一个表单。用户填完表单后,表单将被
    递交给receive.iphtml。当它列表显示结果后,你将回到selection中。

    但是我不能在这里演示插入、更新或删除SQL语句,因为我不能公开该数据库的
    内容。

    <?

    my $dbh = DBI->connect(\"DBI:mysql:test:localhost\", \"\",\"\",

    { PrintError => 0}) || die $DBI::
    errstr;

    !>//

    <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">



    <HTML>

    <HEAD>

    <TITLE>An ePerl Database Example: Fiddling with
    Albums</TITLE>

    </HEAD>

    <BODY>



    <P>

    Use the following form to query the contents of an
    (admittedly limited)

    database of CD albums of mine.



    <TABLE>

    <TR>

    <TD><?=${ \\( search_by_band ( \\$dbh ) ) }!></TD>

    <TD><?=${ \\( search_by_year ( \\$dbh ) ) }!></TD>

    </TR>

    </TABLE>



    <FORM ACTION=receive.iphtml METHOD=POST>

    <B>Or, type in a title:</B> <INPUT NAME=title SIZE=20>

    (for all titles, just put the cursor in this field
    and hit ENTER)

    </FORM>



    </BODY>

    </HTML>

    <?



    $dbh->disconnect;



    !>//

    <?



    sub search_by_band {



    #

    # Note that I passed a reference to the database handle dbh.

    # This means that, in order to reference it within this

    # subroutine, I\"ll have to refer to it as $$dbh.

    #

    my $dbh = shift;

    my $return = \"\";



    #

    # The \"distinct\" keyword in SQL will only return one row for
    a set of

    # identical matches. \"Order by\" will sort the
    # returned set alphabetically.

    #



    my $SQL = <<\"EOT\";

    select distinct artist

    from albums

    order by artist

    EOT



    my $cursor = $$dbh->prepare($SQL);

    $cursor->execute;



    $return .= \"<FORM ACTION=receive.iphtml METHOD=POST>\\n\";

    $return .= \"<B>Pick an artist:</B><BR>\\n\";

    $return .= \"<SELECT NAME=artist>\\n\";



    my @fields;

    while ( @fields = $cursor->fetchrow ) {

    $return .= \"<OPTION>$fields[0]\\n\";

    }

    $return .= \"</SELECT><BR>\\n\";

    $return .= \"<INPUT TYPE=SUBMIT NAME=artist_submit
    VALUE=\\\"Go Search on This Artist!\\\">\\n\";

    $return .= \"</FORM>\\n\";

    }



    sub search_by_year {



    my $dbh = shift;

    my $return = \"\";





    #

    # If COLUMN is defined as a date datum, then year(COLUMN)
    will return only

    # the year portion of the data in the column. \"Order by
    COLUMN desc\"

    # will reverse the usual sort order.

    #



    my $SQL = <<\"EOT\";

    select distinct year(released)

    from albums

    order by released desc

    EOT



    my $cursor = $$dbh->prepare($SQL);

    $cursor->execute;



    $return .= \"<FORM ACTION=receive.iphtml METHOD=POST>\\n\";

    $return .= \"<B>Or, pick a year:</B><BR>\\n\";

    $return .= \"<SELECT NAME=year>\\n\";



    my @fields;

    while ( @fields = $cursor->fetchrow ) {

    $return .= \"<OPTION>$fields[0]\\n\";

    }

    $cursor->finish;



    $return .= \"</SELECT><BR>\\n\";

    $return .= \"<INPUT TYPE=SUBMIT NAME=year_submit
    VALUE=\\\"Go Search on This Year!\\\">\\n\";

    $return .= \"</FORM>\\n\";

    }



    !>//
     

    六、 用CGI.pm - receive.iphtml处理表单结果

    <?

    my $cgi = new CGI; # 利用 \"param\" 解码方法的优势



    my $dbh = DBI->connect(\"DBI:mysql:test:localhost\", \"\",\"\",

    { PrintError => 0}) || die $DBI::errstr;

    !>//

    <!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">



    <HTML>

    <HEAD>

    <TITLE>Results from the Database Search</TITLE>

    </HEAD>

    <BODY>



    <P>

    <?=${\\( display_query_results(\\$dbh, \\$cgi) )}!>//



    <P>

    <A HREF=selection.iphtml>Return to the query page</A>



    </BODY>

    </HTML>

    <?



    $dbh->disconnect;



    !>//

    <?



    sub display_query_results {



    #

    # 注意我将引用传递给数据库处理器dbh和
    # cgi对象 - 这意味着要想在子程序中引用他们

    # 我必须使用$$dbh和$$cgi.

    #

    my $dbh = shift;

    my $cgi = shift;



    my $return = \"\";

    my $SQL;



    if ( defined($$cgi->param(\"title\")) ) {



    my $SQL = <<\"EOT\";

    select title, artist, year(released)

    from albums

    where ucase(title) like ${ \\($$dbh->quote(uc($$cgi->
    param(\"title\")) . \"%\") ) }

    order by title, artist

    EOT



    #

    # 在上述语句中我使用了更复杂的SQL。SQL不会
    # 自动将返回的结果排序,但是利用

    # \"order by\" 语句可以很容易地实现排序 - 只需设定你希望显示的列数

    # 以及它们,的顺序优先级,\"Like\"可以匹配子程序 -

    # 如果你提供了标题 \"abc,\" 则所有标题以\"abc\"开头的唱片

    # 都会被返回。为了消除这种现象,我
    # 在SQL中使用ucase(title),以及uc($$cgi->param(\"title\"),
    将两个字符串都大写显示。

    # %字符是一个通配符,很象UNIX中的*

    # 文件名globbing.

    #

    my $cursor = $$dbh->prepare($SQL);

    $cursor->execute;



    $return .= \"<TABLE BORDER>\\n<TR><TH COLSPAN=3>\";

    $return .= \"<B>Matches on the title search for:
    <TT><I>${ \\($$cgi->param(\"title\") )}</I></TT></B></TH></TR>\";



    $return .= \"<TR><TH>Title</TH><TH>Artist</TH><TH>
    Year of Release</TH></TR>\\n\";



    my @fields;

    while ( @fields = $cursor->fetchrow ) {

    $return .= \"<TR><TD>$fields[0]</TD><TD>$fields[1]
    </TD><TD>$fields[2]</TD></TR>\\n\";

    }

    $cursor->finish;



    $return .= \"</TABLE>\\n\";



    } else {



    if ( defined($$cgi->param(\"artist_submit\")) ) {



    $SQL = <<\"EOT\";

    select title, year(released)

    from albums

    where artist = ${ \\($$dbh->quote($$cgi->param(\"artist\"))) }

    order by released desc, title

    EOT



    } elsif ( defined($$cgi->param(\"year_submit\")) ) {



    $SQL = <<\"EOT\";

    select artist, title

    from albums

    where year(released) = ${ \\($$dbh->quote($$cgi->param(\"year\"))) }

    order by artist, title

    EOT



    }



    my $cursor = $$dbh->prepare($SQL);
    $cursor->execute;

    $return .= \"<TABLE BORDER>\\n<TR><TH COLSPAN=2>\";
    $return .= (defined($$cgi->param(\"artist_submit\"))?
    (\"<B>Artist: <TT><I>\".
    \"${ \\($$cgi->param(\"artist\")) }</I>\".
    \"</TT></B></TH></TR>\\n<TR>\".
    \"<TH>Album Title</TH>\".
    \"<TH>Year of Release</TH></TR>\\n\"):
    (\"<B>Year of Release: <TT><I>\".
    \"${ \\($$cgi->param(\"year\")) }</I>\".
    \"</TT></B></TH></TR>\\n<TR>\".
    \"<TH>Artist</TH><TH>Album Title</TH>\".
    \"</TR>\\n\"));

    my @fields;
    while ( @fields = $cursor->fetchrow ) {
    $return .= \"<TR><TD>$fields[0]</TD>\";
    $return .= \"<TD>$fields[1]</TD></TR>\\n\";
    }
    $cursor->finish;
    $return .= \"</TABLE>\\n\";
    }

    $return;
    }

    !>//


    本教程到此就结束了,但是它只是为了数据库编程的入门,而不是结束。我希望该教程能对你有所帮助。

    发布人:netbull 来自:Linux数据库应用指南