Forums

Python webpage -please help

Hi I am very new in python programming.I am trying make a basic webpage where I ask user to input url like "http://www.google.ca" and a description of the url like"search engine". Using python script I will add that url and description in dbm database and print that url as a link in the browser.I am not sure how python print that url as a link from its dbm database.I am using cgi as the form processor.I will really appreciate if anyone could help me out here.Thanks

Your script will need to generate the HTML of the response page, including a link like this:

<a href="url">description</a>

Are you writing a raw WSGI application or are you using some sort of framework, like Django or Flask? Many of these systems have templating engines to make your life easier. So, I can't really give you a specific answer without knowing a few more details about what you're using.

Since this comes from a database, you'll also need to open the database to retrieve the values. In general it's considered good practice to keep a "pool" of such connections available so requests don't need to open a new one each time, but since you're keeping things simple I would just create a new connection on each request for now.

So, an example of what you're doing if you weren't using a templating engine and if the database key was the URL and the value was the description:

def show_links():
    response = "<html><head><title>Links</title></head><body><ul>"
    db = anydbm.open("my_db_file")
    for url, description in db.items():
        respose += "<li><a href=\"%s\">%s</a></li>" % (url, description)
    db.close()
    response += "</li></body>"
    return response

This is example isn't brilliant for production code, but you seem to be looking for a simple illustration to get you started so I've kept it as simple as I can. For example, you should HTML-escape the description value before placing it in the page, and URL-escape the URL value. I've left those as an exercise! (^_^)

robinclarke95 -- if you're completely new to Python, then I definitely recommend you learn one of the web application frameworks. A good way to get started would be to try our "I want to create a web application" guide, which you can find on the help page

I agree, although if this is purely a learning exercise it can be instructive to do it from scratch first before taking on a framework. It depends what level of learning you're after.

Mind you, I'm someone who isn't really comfortable writing in any language until I can see how it would map down to hardware, at least in principle, so maybe you don't want to worry about how I go about learning things.

Hi,thank you so much for helping me here.Now I learned how to print the dbm key and value from database.I almost gave up on this problem.I only have very little experience in basic object oriented programming.I do not have much knowledge in any framework too.I started learning this webpage application very recently and got this problem to solve.I am not supposed to use any framework.My program will run in Apache server using raw python.

To add the input value to the database I wrote this basic code,but does not seem to work.I agree this solution may seem to you really bad,but I am not sure where to learn this,please tell me what am I doing wrong

#!/usr/bin/python
import cgi
import anydbm
form = cgi.FieldStorage()
print("content-type: text/html")
print

 print """
<html>
<head><title>test</title></head>
<body>
<form>
<form method="Post"
action="/cgi-bin/helloworld.py">
urlname: <input type="urlname" name="urlname">
<input type="submit" value="Submit">
</form>
</body>
</html>
"""

#to start with, if I want to add one by one (just to see if it works)
urlname = form.getvalue("urlname")
urldescription=form.getvalue("urldescription")
file=anydbm.open('my_db_file') 
file['url']= 'urldescription'  # add the input  from the user to the dbm database and I will print these as you showed in your solution

Is this a homework question, by any chance? I'll try and give you a hand, but I'm not going to write the whole thing for you... (^_^)

When you say your script is running under Apache using "raw python", I'm not sure if you mean as a WSGI application (using mod_wsgi) or as a CGI application. Since you're using the cgi module I'm going to assume the latter, but for future reference if you're using the CGI interface then make sure you say so, because it's relatively uncommon these days.

It looks like you've figured out most of the pieces you need, but they're not quite put together in the right way. The point you might be missing is that your script needs to handle two different types of HTTP request - when the user first loads the page they make a GET request, and when they submit form values they make a POST request. Also, it looks from the description like you need to support multiple pages:

  • Listing the bookmarks
  • Editing a bookmark
  • Adding a new bookmark
  • Result of validating bookmarks

Let's just get adding a bookmark working right now, though.

You could have multiple CGI scripts at different URLs for this job, but I'm going to assume you need one script to do it all. Because this is CGI, that means using query parameters to differentiate functions. So, it goes like this:

  1. User enters the URL of your page into their browser.
  2. Browser sends a GET request which goes to your CGI script.
  3. Your script responds with a page listing the bookmarks and a link to the "add bookmark" page.
  4. User clicks the link to add a bookmark.
  5. Browser sends a GET request for the same page with a query parameter indicating the "add bookmark" page is requested.
  6. Your script responds with a page showing a form to add a new bookmark.
  7. User fills in form and hits "submit".
  8. Browser sends a POST request to the action URL of the form, which is your script.
  9. Your script receives the POST and adds the item to the database, and then responds with something - either the bookmark listing page again, or a simple page that says "bookmark added" and contains a link back to the bookmark page (a redirect back to the bookmarks page would be ideal, but I'm trying to keep things simple).

By the way, it looks like from your spec that the description should be the key and the URL the value. Doesn't really matter either way, but I've amended it in my example code below.

To achieve the above, you'll need to detect whether your script has received any arguments either in query parameters or the body of a POST request, and serve different pages based on that. So the skeleton of your script would look something like this:

#!/usr/bin/python
import cgi
import anydbm
form = cgi.FieldStorage()
print("content-type: text/html")
print
db_file = anydbm.open('my_db_file')

if 'urlname' in form and 'urldescription' in form:

    urlname = form.getvalue('urlname')
    urldescription = form.getvalue('urldescription')
    db_file[urldescription] = urlname

    # --> Print a "bookmark added" page here with link back to list page

elif 'addbookmark' in form:

    # --> Print page with form to add bookmark here

else:

    # --> Print page listing bookmarks here

db_file.close()

I've left printing the pages for you to do, although your example above shows that you know how to print HTML pages so you should be fine. Remember you'll need a link on the bookmark listing page back to a URL something like /cgi-bin/helloworld.py?addbookmark=1 - this link will take the user to the "add a bookmark" page.

By the way I should add that this is the simplest solution I could think of, not necessarily the best solution - but if you're still learning the basics then let's keep things simple.

One thing I should add here is that if you need to use CGI, it won't work on PythonAnywhere -- we have a hosted environment, and only support WSGI-based apps.

That was a great help!!..you made it so crystal clear now I know what is the problem asked.Thank you so much for the nice explanation, you are 100 times better than my prof !!!. You are right ,it is a homework.By mistakenly I have taken a wrong elective class(this is a final year class) and this class did not have any prerequisite for programming knowledge.My bad luck,,, But I have learned a lot of thing in last few days though..

I have added some codes where you wanted me to add codes),it works good, but I think I am doing something wrong .It does not add the inserted url and description in the database and do not show the form element in the (" if 'urlname' in form and 'urldescription' in form: ") condition area.I have used the post method in" elif 'addbookmark' in form: " condition so that browser send the submit request to process.

For now I added url as keys,later I will change the description as key and url as value. Could you please take a look .Thank a lot for helping me:)

#!/usr/bin/python
import cgi
import anydbm
form = cgi.FieldStorage()
print("content-type: text/html")
print

db_file = anydbm.open('my_db_file','c')
db_file['http://www.google.ca/']='search'
db_file['http://ca.yahoo.com/?s=https']='homepage'
db_file.close()


def show_links():
         response = "<html><head><title>Links</title></head><body><ul>"
         db_file = anydbm.open("my_db_file")
         for url, urldescription in db_file.items():
         response += "<li><a href=\"%s\">%s</a></li>" % (url, urldescription)
         db_file.close()
         response += "</ul></body></html>"
         return response

if 'urlname' in form and 'urldescription' in form:
         urlname = form.getvalue('urlname')
         urldescription = form.getvalue('urldescription')
         db_file = anydbm.open("my_db_file")
         db_file[urlname] = urldescription
         print 
         print """
         <html>
         <head><title>test</title></head>
         <body><p>Bookmark Added</p>
         <a href ="http://localhost:8000/cgi-bin/freshstart.py"<p>Go Back</a> 
         </body>
         </html>
         """

elif 'addbookmark' in form:
         # --> Print page with form to add bookmark here
         print """
         <html>
         <head><title>test</title></head>
         <body>
         <form>
         <form method="Post"
         action="/cgi-bin/freshstart.py">
          #-->7.User fills in form and hits "submit".
          #-->8.Browser sends a POST request to the action URL of the form, which is your script.
          urlname: <input type="url" name="urlname">
          urldescription:<input type="urldescription" name="urldescription">
          <input type="submit" value="Submit">
          </form>
          </body>
          </html>
    """

else:
        newresponse=show_links()
        print newresponse
        print"""
        # --> Print page listing bookmarks here
        <html>
        <head><title>test</title></head>
        <body>
        <form>
        <form method="get"
         action="/cgi-bin/freshstart.py">

         <input type="submit" value="Add Bookmark" name="addbookmark">
         </form>
         </body>
         </html>
          """

          db_file.close()

Is the file you've written exactly as it's shown above? If so, the indentation (the spaces at the start of the line) is wrong on at least some of the if blocks, that might be causing your problems.

So, part of your code looks like this:

if 'urlname' in form and 'urldescription' in form:

urlname = form.getvalue('urlname')
urldescription = form.getvalue('urldescription')
db_file[urlname] = urldescription

... but it should look like this:

if 'urlname' in form and 'urldescription' in form:

    urlname = form.getvalue('urlname')
    urldescription = form.getvalue('urldescription')
    db_file[urlname] = urldescription

This is because Python uses the indentation to decide which lines are inside the if, and which lines are after it. In many languages the indentation is just to make the code look pretty, but in Python it's important.

Similarly, for the show links function, you need to indent it exactly as I did above.

Try fixing those and see if it works better.

Hi Cartroo ,thanks a lot for looking .I have the indentation as you said, When I pasted ,did not indent nicely. I tried to print some text in this particular if statement ,it prints everything until hits the line" urlname = form.getvalue('urlname')". I have been trying different thing ,but does not seem to work .Is my CGI not working while form processing.

Hello,

The way I debug these things is to use the Python dir() command, to look inside objects. So, at the url= line, put a print dir(form), so you can see what's in the object

Cheers, Robert

@rcs1000: That suggestion is a good one, although I'm still a little suspicious that it's the first line in an indented block which is failing to run.

@robinclarke95: Before spending too long debugging this, I would suggest checking the indentation and syntax. Fire up a bash shell and run pyflakes myscript.py where myscript.py is your CGI script. See if that reports any errors - it shouldn't report anything.

As an aside for any Vim users, there's an excellent Vim plugin which performs Python syntax checking in real-time in your browser. It'll even catch unused imports and variables.

thank you so much for helping me! I do not have that indented block anymore.I also fixed the other indentation.Still the same.I should definitely use the techniques you have suggested.I will get back as soon as I do the Python syntax checking.I am learning a lot of new things from you!

@Cartroo ,I have added "c" in line db_file = anydbm.open("my_db_file","c"), and it is working all perfect now.I see all the html form on this page now.once I hit "goback" I see that bookmark has been added.Now I will do the next step.Thank you so much for helping me out.I am very glad.

I installed VIM (from this vim.org) ,then installed pyflakes-vim.VIM has 3 items- vim73 folder, vimfiles folder and _vimrc file. In my pyflakes-vim zip file there is one ftpplugin folder.Do you suggest that I should extract this pyflakes-vim zip file into the vim73->ftplugin folder of vimfiles ->ftplugin folder. ( sorry I am bugging you ,I don't have much idea about how to run this VIM).For now I have added this extracted pyflakes-vim and kept pyflakes and pyflakes.vim into my python running folder.and then in shell command window typed pyflakes freshstart.py,but it says invalid sytax

        >>> pyflakes freshstart.py
        SyntaxError: invalid syntax

@Robert,thanks for your suggestion.I am learning lots of new stuff!

Hi Cartroo, I have made a lot of change in my program. I am stuck in a place,I am trying to create a variable that I can change in if,elif statement.I created a variable called track and if I have input from my user(the bookmark number they click on) I will save it in the track variable.But when I call this variable from another elif function, it gives me the value track=0.I tried to put "global track" at the beginning,still does not work.Could you please tell me how can I fetch a variable value.Thank you!

#!/usr/bin/env python
dict={}
track=0
print("content-type: text/html")
print

def show_links():
    response = """<html><head><title>Links</title></head><body><form><form method="get"
    action="/cgi-bin/freshstart2.py"><table border=1><tr><td>Bookmark</td><td>Edit</td></tr>"""
    count=1
    db_file = anydbm.open("my_db_file")
    for url, urldescription in db_file.items():
    response += "<tr><td><a href=\"%s\">%s</a></td><td><input type=submit value=%s  name=edit></td></tr>"%(url,urldescription,count)
    count=count+1
    db_file.close()
    response += "<ul></table></form></body></html>"
    return response

def dict_item():#created a dictionary where I can  save number as my key.these numbers corresponds to each entry of bookmark.
     count=1
     db_file = anydbm.open("my_db_file")
     for url,urldescription in db_file.items():
     dict[count]=[url,urldescription]
     count=count+1
     return dict

 elif  'edit' in form:
     iterate=dict_item()
     x=eval(form['edit'].value)
     y=iterate[x][0]
     z=iterate[x][1]
     track=x
     print track
     print"""
     <html>
     <head><title>test</title></head>
     <body>
     <form>
     <form method="Post"
     action="/cgi-bin/freshstart2.py">
     Edit Url: <input type="url" name="urledit"  value=%s>""" %(y)
     print"""
     Edit Description:<input type="text" name="des_edit" value=%s>"""%(z)
     print"""
     <input type="submit" value="Submit"><br><br>
     <input type="submit" value="Delete" name="delete">
     </form>
     </body>
     </html>
     """
elif  'urledit' in form:
  {I want to use the value of track save in the previous elif statement }

Firstly, is that your entire program? It looks to me like there's a bit missing - after the dict_item() function, there's an elif which doesn't seem to match any if I can see.

Anyway, about variables. It looks like your track variable is at the top level of the script - this makes it a global variable. For the code at the top level, you can use it however you like. This includes inside if...else, because these aren't functions - they'd usually be referred to as "blocks".

Inside a proper function (i.e. starts with def), you can READ the value of a global variable. However, if you want to SET the value (i.e. track = x) then you need to put global track at the start of your function, like you've said there. If you don't do this, Python will assume that it's a local variable which is only available inside that call to the function, and is automatically deleted once the function exits. Next time you enter the function, local variables are re-created again from scratch.

A function can also take parameters which are put inside the brackets of the function name. If you do this then you need to call the function with these parameters, and what this does is take a copy of the value and essentially make it the same as a local variable inside the function. This means that if the function changes the value, it won't change the original (but with a global it does change). I should mention here that types like lists ([1, 2, 3]) and dictionaries ({"one": 1, "two": 2}) are passed as "references" which means that functions can change them. Don't worry about this too much, but just remember it for the future.

A quick example:

my_global = 1

def my_function(my_parameter):
    global my_global
    my_global = my_global + 1
    my_local = 1
    my_local = my_local + 5
    my_parameter = my_parameter + 10
    print "Parameter: %d" % (my_parameter,)
    print "Local: %d" % (my_local,)
    print "Global: %d" % (my_global,)

my_function(123)
my_function(my_global)
my_global = 100
my_function(123)

This produces the following output:

Parameter: 133
Local: 6
Global: 2
Parameter: 12
Local: 6
Global: 3
Parameter: 133
Local: 6
Global: 101

So, the first time the function is called, the parameter is 123, which is 133 when you add 10. The global starts off as 1, the function adds 1 to it to make 2. The local is always 6 because it's re-created every time the function is called - it starts as 1 and the function then adds 5 to make 6.

Once the function has ended the first time, the value of my_global is 2 because the that's what happens with globals - if the function changes them, they stay changed.

So the second time the function is called, the parameter is actually the value of my_global but because we pass a copy then when the function adds 10 to it, we actually only change the copy that the function has, so the parameter is 12 (because my_global was 2 after the previous call to the function and the function adds 10 to that). The actual value of my_global after the function is 3, because it adds 1 to it again.

Now the main script assigns 100 to my_global, replacing any previous value. So this time when the function adds 1 it gets 101.

Hopefully that clears up some confusion about global variables. The important thing is that if you forget the global line at the start of the function, you might end up with a local variable which has the same name and it "covers up" the global one inside the function. Or you might just get an error, like you do with this script.

However, I don't actually think that's the cause of your problem - I still wanted to explain that because it's really important to understand how variables and functions work in Python. I think your problem is because each time you make a new HTTP request, your script is entirely run again from scratch - so every single variable (including globals!) is reset back to its initial value. If you want to store information between one page and another page, you need to use the database to do that (or make a file on disk or something like that).

This isn't always true in web frameworks, but definitely for CGI scripts your entire program is re-run every time you ask for a new page in the browser - nothing is saved, except what you put in the database. Even in frameworks when your program isn't re-run, you always put things you want to save in a database or in a file on disk, because you don't want to lose anything when your program is restarted.

Does that help?

Hi Cartroo,You got my problem exactly right! I was having trouble understanding why I can not save a value in a variable,or in a list or dictionary. Everytime I switch to a different page like edit or add bookmark,the variable values become the initial value zero.So as you explained ,if I had a running program that do not refreshes from the beginning then I will be able to get that value.Now I understand.Great explanation!,thanks a lot. I really appreciate your help! Last time I just wrote you only part of the code,I finished until adding,editing(both url and description),deleting.It woks good.I used a temporary database where I save only the last clicked url and description and once I use this value in elif block I delete the database.I used a for loop to delete,even though I knew there is only one element in the database,because I could not find any command that will fetch the only key,like get first item.I also used a dictionary to store the number as a key and value as url and description.I have learned so much,in last few days.Thanks to you! I have another question,now I have to make a http head request to verify the url.I am not allowed to use the python library to verify.Only by socket programming,like send,receive,bind those command,I never heard of this before:( My question is when I am writing a socket connection program,then do I write the client side script or server side,if I am writing this Bookmark script, and my server is apache then,is the person who request for this bookmark page is the client.I am trying to read,but can not get exactly what my confusion.I was wondering if you could please give me some hint.Thanks again.(-_-)

Glad to hear you're making progress.

With regards to pulling a single value out of a dictionary, it is possible as a one-liner (e.g. key, value = db.iteritems().next()) but in all honesty using a for loop is probably the most elegant solution. The main issue is that if the dictionary is empty, one-line solutions will typically throw an exception which you'd only have to catch anyway (that one-liner would throw a StopIteration exception if the dictionary is empty).

Anyway, for right now, I would just go with whatever works!

So, you need to make a HTTP request to verify the URLs using only the sockets library? Sounds like your professor is a hard-arse! (^_^)

In answer to your client/server question, you'll be acting as a server for the request to add the bookmark, but you're acting as a client when you check that the URL is valid (where the server in that case is the hostname from the URL). So depending how you look at it, you're acting as both.

I don't really have time right now to go through sockets in detail, but there are plenty of good tutorials about them if you Google it - this one looks alright, for example.

To summarise, though, what you'll need to do is this:

  1. From the URL extract the hostname and the path. For example, in the URL http://www.example.com/data/images/logo.png, the hostname is www.example.com and the path is /data/images/logo.png.
  2. Use the sockets functions to open a TCP connection to the hostname from the URL (all HTTP connections go over TCP, but sockets can be used for other protocols so you need to remember it's TCP you're using).
  3. Down the TCP connection you need to send a HTTP "HEAD" request for the path from the URL.
  4. The server will send back a HTTP header response which tells you whether the URL is valid. You need to read this from the socket, then process it to understand whether it's saying the URL is OK or not.

Looking at the steps in detail, the first step just involves using string processing functions. Python has functions which process URLs for you very easily (the standard one is urlparse), but if your professor wants you to do everything yourself then you might not be allowed to use that - you'll need to check. If you don't use those functions, you can assume that the URL will be http:// followed by the hostname then a / and everything after that is the path. There are more complicated URLs that this, but I would stick to the simple ones for now - you can always improve things later.

Note: There are also URLs starting https:// for SSL (secure) connections, but you can't use simple sockets for this so I'm assuming you don't want to support these for now.

Anyway, step 2 is best done by reading the sockets tutorial. Essentially you:

  1. Create the socket.
  2. Connect the socket to the remote server.
  3. Build a HTTP request as a string.
  4. Send the request over the socket.
  5. Read the socket until you get a response back as a string.
  6. Get the "response code" from the response.

Again, a full description of the HTTP protocol is beyond a simple forum post. The full definition can be found in the RFC, but I'm sure you can Google around to find simple introductions. Overall, your request will look something like this:

HEAD /data/images/logo.pnp HTTP/1.1
Host: www.example.com

The response will be several lines long, but actually you probably only care about the first line, which will look something like this:

HTTP/1.1 200 OK

It's the number 200 you care about there - that means the URL is valid. In fact, if you want to be quick and dirty about it, any number lower than 400 is basically fine. The truth is a little more subtle than that, but that's probably fine for your purposes.

Hope that helps - not sure I'll have much more time to help out with this, I'm afraid, but you've always got Google!

You're quite welcome. Everyone needs a few pushes in the right direction to get going.

Best of luck with the rest of your courses, and I hope you carry on with Python for any other coding you need to do - it's a really nice language.