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.

Hey TecMint readers,

Exciting news! Every month, our top blog commenters will have the chance to win fantastic rewards, like free Linux eBooks such as RHCE, RHCSA, LFCS, Learn Linux, and Awk, each worth $20!

Learn more about the contest and stand a chance to win by sharing your thoughts below!

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.

Each tutorial at TecMint is created by a team of experienced Linux system administrators so that it meets our high-quality standards.

Join the TecMint Weekly Newsletter (More Than 156,129 Linux Enthusiasts Have Subscribed)
Was this article helpful? Please add a comment or buy me a coffee to show your appreciation.

11 Comments

Leave a Reply
  1. 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.

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

      Reply
      • 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,

        Reply
  2. 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.

    Reply
  3. 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

    Reply
    • @Dead Fred,
      I just finished revising the entire series and addressed the issues you encountered. The application is now working properly from start to end :)

      Reply
  4. 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!

    Reply
    • Feel free to contact me. I’ll be happy to help. Consider this a minor setback that is worth understanding and fixing for the sake of starting out with Django.

      Reply
    • 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.

      Reply

Got Something to Say? Join the Discussion...

Thank you for taking the time to share your thoughts with us. We appreciate your decision to leave a comment and value your contribution to the discussion. It's important to note that we moderate all comments in accordance with our comment policy to ensure a respectful and constructive conversation.

Rest assured that your email address will remain private and will not be published or shared with anyone. We prioritize the privacy and security of our users.