How to Create Mobile-Friendly Web Applications using Django Framework – Part 3

“This article is revised and updated with latest version of Django – May 2016”

In Part 1 of this series you learned how to install and configure Django in a virtual environment and you created the skeleton of your first project.

Then in Part 2 we created an application and a model for Post objects, which we later migrated to the database. Finally, we showed you how to integrate your newly created application to the Django administration user interface.

These articles are part of Django series:

Installing and Configuring Django Web Framework with Virtual Environments – Part 1

Reviewing Python Basics and Creating Your First Web Application with Django – Part 2

Creating Mobile Friendly Web Application with Django

Creating Mobile Friendly Web Application with Django – Part 3

In this final guide we will discuss how to access the application using the UI and how to make it mobile-friendly for all kind of devices. That said, let’s get started.

Creating objects via the Django admin interface

To create objects of type Post (remember that is the model we defined in Part 2 of this series), we will use the Django admin interface.

Make sure the Django built-in web server is running on port 8000 (or another one of your choosing) by running the following command from the outer myfirstdjangoproject directory:

# cd ~/myfirstdjangoenv/myfirstdjangoproject
# python manage.py runserver 0.0.0.0:8000

Now open your web browser and point to http://ip-address:8000/admin, then log on using the credentials you set up in the previous article and start writing a post (which, again, will create an object of type Post and insert the associated data into the underlying database):

Django Administration

Django Administration

Repeat the process 2 or 3 times:

Create Object in Django

Create Object in Django

After we have created a couple of posts, let’s see what we need to do in order to display them using our web browser.

Our initial view

Our first view (~/myfirstdjangoenv/myfirstdjangoproject/myblog/views.py) will be in charge of filtering all Post objects and returning those where the value of whenPublished is less than or equal to the current date and time (whenPublished__lte=timezone.now()) ordered by descending whenPublished, which is the same as saying “latest first“.

These objects are saved into a variable conveniently named posts, and are returned (identified as allposts) to be embedded in the HTML, as we will see in the next section:

from django.shortcuts import render
from .models import Post
from django.utils import timezone
def posts(request):
        posts = Post.objects.filter(whenPublished__lte=timezone.now()).order_by('-whenPublished')
        return render(request, 'myblog/posts.html', {'allposts': posts})

Finally, the double underscore in whenPublished__lte above is used to separate a database field (whenPublished) from a filter or an operation (lte = less than or equal).

Once we have defined our initial view, let’s work on the associated template.

Create Template for our first Project

Following the directives and paths given in the previous section, we will store our initial template inside myblog/templates/myblog. This means you will need to create a directory named templates and a subdirectory called myblog:

# cd ~/myfirstdjangoenv/myfirstdjangoproject/myblog
# mkdir -p templates/myblog

We will call the template posts.html and insert the following code in it. You will notice that we are adding online references to jQuery, Bootstrap, FontAwesome, and Google fonts.

In addition, we have enclosed Python code inside curly brackets inside the HTML. Please note that for every object of type Post we will show its title, its published date and author, and finally its text. Finally, in red you will see that we make a reference to the objects returned via myblog/views.py:

Ok, here’s the posts.html file:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link href='https://fonts.googleapis.com/css?family=Indie+Flower' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Pacifico' rel='stylesheet' type='text/css'>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" type='text/css'">
<script src="https://code.jquery.com/jquery-2.1.4.min.js">
</script>
    <style>
      .title {
        font-family: 'Indie Flower', serif;
        font-size: 30px;
        color: #1E90FF;
      }
      h1 {
        font-family: 'Pacifico', serif;
        font-size: 45px;
        color: #1E90FF;
      }
    </style>
</head>
<body>
<div class="container"><h1>My blog</h1><br>
{% for post in allposts %}
    <div>
        <div class="title">{{ post.title }}</div>
        <strong>Published on {{ post.whenPublished }} by {{ post.author }}.</strong>
        <p>{{ post.text|linebreaks }}</p>
    </div>
{% endfor %}
</div>
</body>
</html>

In the above template, the linebreaks filter is used to replace line breaks in plain text with the corresponding HTML equivalent (<br /> or </p>) to format each post properly with paragraph separation.

Next, we need to set up a mapping between URLs in our application and the corresponding views that return the data. To do so, create a file named urls.py inside myblog with the following content:

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.posts, name='posts'),
]

The r'^$' deserves a little more explanation. The leading r instructs Django to treat the string inside single quotes as a regular expression.

In particular, r'^$' represents an empty string so that when we point our browser to http://ip-address:8000 (and nothing else), the data returned by the variable posts inside views.py (refer to the previous section) will be presented in our home page:

Last, but not least, we will include the urls.py file of our blog application (~/myfirstdjangoenv/myfirstdjangoproject/myblog/urls.py) into the urls.py of our main project (~/myfirstdjangoenv/myfirstdjangoproject/myfirstdjangoproject/urls.py):

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('myblog.urls')),
]

Then let’s start the web server:

# cd ~/myfirstdjangoenv/myfirstdjangoproject
# python manage.py runserver 0.0.0.0:8000

We should now be able to see the lists of posts we created earlier:

Check My Django Web Project

Check My Django Web Project

Thanks to Bootstrap, you can still have an excellent visualization in a smaller device:

Mobile Responsive Look of My Project

Mobile Responsive Look of My Project

Summing Up

Let’s now review the concepts that we have covered in this article and throughout this series:

1. Each model defines an object and maps to a database table, whose fields in turn map to the properties of that object. On the other hand, a template defines the user interface where the data returned by the view will be displayed.

Let’s say we want to modify our model by adding a field named summary to the Post object, where we will store an optional brief description of each post. Let’s add the following line in myblog/models.py:

summary = models.CharField(max_length=350, blank=True, null=True)

As we learned in the previous article, we need to migrate the changes to the database:

# python manage.py makemigrations myblog
# python manage.py migrate myblog
Django: Migrate Changes to Database

Django: Migrate Changes to Database

Then use the admin interface to edit the posts and add a brief summary to each post. Finally, replace the following line in the template (posts.html):

<p>{{ post.text|linebreaks }}</p>

with

<p>{{ post.summary }}</p>

Refresh the home page to see the changes:

Django: Verify Changes to Web Project

Django: Verify Changes to Web Project

2. A view function takes a HTTP request and returns a HTTP response. In this article, def posts(request) in views.py makes a call to the underlying database to retrieve all posts. If we want to retrieve all posts with the word ansible in the title, we should replace.

posts = Post.objects.filter(whenPublished__lte=timezone.now()).order_by('-whenPublished')

with

posts = Post.objects.filter(title__icontains="ansible").order_by('-whenPublished')

By separating the user interface from the application logic in web applications, Django facilitates the tasks of maintaining and escalating apps.

3. If you followed the instructions provided in this series, the structure of your project should be as follows:

myfirstdjangoenv/myfirstdjangoproject
├── db.sqlite3
├── manage.py
├── myblog
│   ├── admin.py
│   ├── admin.pyc
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── 0001_initial.pyc
│   │   ├── __init__.py
│   │   └── __init__.pyc
│   ├── models.py
│   ├── models.pyc
│   ├── templates
│   │   └── myblog
│   │       └── posts.html
│   ├── tests.py
│   ├── urls.py
│   ├── urls.pyc
│   ├── views.py
│   └── views.pyc
└── myfirstdjangoproject
    ├── __init__.py
    ├── __init__.pyc
    ├── settings.py
    ├── settings.pyc
    ├── urls.py
    ├── urls.pyc
    ├── wsgi.py
    └── wsgi.pyc

In case the above list does not display correctly in your browser, here’s a screenshot of the output of the following command:

# tree myfirstdjangoenv/myfirstdjangoproject
My Django Protect Tree

My Django Protect Tree

Summary

Although all of these concepts may seem a little intimidating at first, I can assure you Django is well worth all the efforts necessary to become acquainted with it

I hope that the example that we have used in this series to introduce you to this outstanding web framework will motivate you to learn more. If so, the official Django documentation (which is constantly kept up to date) is the best place to start.

I can assure you that there is a whole lot more to Django than we can adequately cover in a series of articles, so feel free to explore it and learn by doing!

Feel free to drop us a note with questions or suggestions using the form below.

Best Affordable Linux and WordPress Services For Your Business
Outsource Your Linux and WordPress Project and Get it Promptly Completed Remotely and Delivered Online.

If You Appreciate What We Do Here On TecMint, You Should Consider:

  1. Stay Connected to: Twitter | Facebook | Google Plus
  2. Subscribe to our email updates: Sign Up Now
  3. Get your own self-hosted blog with a Free Domain at ($3.45/month).
  4. Become a Supporter - Make a contribution via PayPal
  5. Support us by purchasing our premium books in PDF format.
  6. Support us by taking our online Linux courses

We are thankful for your never ending support.

Gabriel Cánepa

Gabriel Cánepa is a GNU/Linux sysadmin and web developer from Villa Mercedes, San Luis, Argentina. He works for a worldwide leading consumer product company and takes great pleasure in using FOSS tools to increase productivity in all areas of his daily work.

Your name can also be listed here. Got a tip? Submit it here to become an TecMint author.

RedHat RHCE and RHCSA Certification Book
Linux Foundation LFCS and LFCE Certification Preparation Guide

You may also like...

11 Responses

  1. Hans says:

    I run this on a remote server using port 443 instead (I can access the admin page and create Posts and so on) Anyhow , everything went well until I should run the post in the browser. Then I ended up at the default server page. Any suggestions. I run this on a Ubuntu 14,4 using Django 1.9.

    • @Hans,
      1) How are you accessing the post in the browser?
      2) Can you paste here a directory listing of your project?
      Best,

      • Hans says:

        I try to access the page through http://public-ip-address:443 ending up with the default web server page. When I run http://public-ip-address:443/admin it works fine and the admin interface open up.

        I have turned the firewall off and anyhow i have opened up port 443 just in case. A pwd in myfirstdjangoproject shows /home/django/myfirstdjangoproject.

        I have created this in the django folder where I have my other projects (which is accessible) so it there is a working virtual environment already. Perhaps I need to create a new one. Anyhow I run the server manage.py from within the myfirstdjangoproject

        Best Regards,

  2. reader says:

    It would be nice to see all included files and folders listed in the end of this article.
    Just to make it easier to check one has created all files to correct folders.
    There are several folders with same name, so for example should urls.py be in myblog or myblog/tenplates/myblog?
    I think in the first folder.

  3. Dead Fred says:

    Gabriel,
    I finally got a few moments to play with this. I had to do the following to make it work.

    In myfirstdjangoproject/urls.py, I added 2 lines:

    import myblog
    url(r’^$’, include (‘myblog.urls’)),

    – DF

  4. Dead Fred says:

    Thanks for the reply Gabriel. The file was created under ~/myfirstdjangoenv/myfirstdjangoproject/myblog/ along with the views.py. I did a copy paste of the article text and was careful to make sure spacing was correct. Interestingly, all of the other .py files have matching .pyc files, but views.py and urls.py do not. Don’t know if that is a problem or not.

    I will keep trying to figure it out. I did something yesterday and saw that django seemed to be aware of the admin stuff but not the urls.py. Don’t remember what or where I was at the time. May have been when I was trying to turn the debug info off. Not sure. Will play for a while, don’t want to trouble you with the upload, but may have to at some point.

    Thanks!

  5. Dead Fred says:

    All well until I try to pull the web site up, http://192.168.1.52:8000. The admin posts seemed to have worked but no content displayed other than the default “It worked!” page.

    Thanks for the tutorial!
    – DF

    • Did you create the urls.py file as instructed in this guide? If so, and it still isn’t working, feel free to make a tarball of your entire project, upload it somewhere and send me the link. I’ll be glad to take a look.

Got something to say? Join the discussion.

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.