24. Using the ORM in views.py

We’ve now looked at the underlying database and building a simple normalized database. We’ve also briefly looked at how to add some data to your database via the administration interface, or via a script that you might write.

This should hopefully give us enough background knowledge to allow us to consider how to work with data inside our web application at runtime.

Let’s use this opportunity to build a simple web page, that can show the contents of our database to the users.

Firstly we navigate in VSC to bioweb/urls.py as we want to send various user requested paths to our web application. We need to add path(”, include(‘genedata.urls’)) here:

urlpatterns = [
    path('', include('genedata.urls')),
    path('admin/', admin.site.urls),
]

We also need to ensure we are importing include too:

from django.urls import include, path

We’re making a promise that there is a urls file within genedata here, so we need to go to the genedata folder and create a new urls.py file here.

from django.urls import include, path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

If we run the server now in CMD, we will get this error:

AttributeError: module 'genedata.views' has no attribute 'index'

In our web app in the views file we don’t have anything called index. We’ve promised this function exists, but it doesn’t yet, so we can go ahead and fix that quite simply.

In genedata/views.py we can add a simple test response:

def index(request):
    response = "Hello"
    return render(request, 'genedata/index.html', {'message': response})

Once we save views.py this will trigger a reload of the server and the error message disappears.

What we need to do now is create the template that it needs to use.

Inside the genedata folder we can create a new folder called templates, and inside here another new folder called genedata. Then inside here we can create a new index.html file and set this up as follows:

<html>
    <head>
    </head>
    <body>
        <p>
        {{ message }}
    </p>
    </body> 
    </html>

If I now run the server and try to go to http://127.0.0.1:8000/ I may find that the host is not allowed. I will need to go back and add localhost and 127.0.0.1 to the allowed hosts in settings.py:

ALLOWED_HOSTS = [
    'localhost',
    '127.0.0.1',
]

Now if I go to http://127.0.0.1:8000/ I will see that I get the word hello returned. This is because the variable response has been set to ‘hello’ in views.py, and this is returned in the next line of the method on that page. Viewing the page source will see our template, with hello instead of {{ message }}.

So now we know the template works, we want to make use of the models which read the database. So back to views.py and add

from .models import *

and then put this in the index function:

def index(request):
    genes = Gene.objects.all()
    return render(request, 'genedata/index.html', {'genes': genes})

genes is asking for objects.all – it’s waht Django calls a Query Set. It’s an iterable object that represents the results of a database query.

objects.all is very much like the SQL command SELECT *

In this instance, you can essentially think of it as a python list where each element in the list is one row from the gene database table.

The database query is not run, however, until we attempt to iterate over this object.

We can now jump back to index.htm; and show the user the data in this object by making this change:

<html>
    <head>
    </head>
    <body>
        <h1>D.Bucha Genes</h1>
        <table>
        <tr><th>Gene ID</th></tr>
        {% for gene in genes %}
        <tr><td>{{gene.gene_id}}</td></tr>
        {% endfor %}
        </table>
    </body>
</html>

This part is the iteration:

        <tr><th>Gene ID</th></tr>
        {% for gene in genes %}
        <tr><td>{{gene.gene_id}}</td></tr>

D.Bucha is a made up name for this exercise. Now if we go back to http://127.0.0.1:8000/ we’ll see a list of genes.

We’re seeing the list of genes called gene_id we specified that name in the template. Because in the Gene class in models.py we added this:

    def __str__ (self):
        return self.gene_id

we could get the same result if we just wrote {{gene}} – it returns gene_id.

We could show more detail by having a page for each gene, like this in our template:

<tr><td><a href="/gene/{{gene.pk}}" >{{gene}}</a></td></tr>

We now have links and we need to create the page to show these.

In urls.py we can add a new path which handles requests for genes:

urlpatterns = [
    path('', views.index, name='index'),
    path('gene/', views.gene, name='gene'),
]

We will also need to go to views.py and add a new function called gene:

def gene(request, pk):
    gene = Gene.objects.get(pk=pk)
    return render(request, 'genedata/gene.html', {'gene': gene})

What’s happening here? In urlpatterns the new path is capturing an integer where it says . In views we then need to check that this pk equals the same pk in our database, hence the (pk=pk).

It’s like the SQL command SELECT * WHERE pk=this value we passed to it (we passed it in def gene(request, pk). It’s just confusing that we called it pk here too)

We can see now that a path to gene/1 will need to go to a template in genedata called gene.html so we will need to create that.

<html>
    <head>
    </head>
    <body>
        <h1>{{gene}}</h1>
        <table>
        <tr><th>Key</th><th>Value</th></tr>
        <tr><td>Entity: </td><td>{{gene.entity}}</td></tr>
        <tr><td>Start: </td><td>{{gene.start}}</td></tr>
        <tr><td>Stop: </td><td>{{gene.stop}}</td></tr>
        <tr><td>Sense: </td><td>{{gene.sense}}</td></tr>
        <tr><td>Start Codon: </td><td>{{gene.start_codon}}</td></tr>
        </table>
    </body>
</html>

This then creates a template and we can see the data on each page.

Wednesday 3 November 2021, 26 views


Leave a Reply

Your email address will not be published. Required fields are marked *