How to build a photo feed using Django and Pusher
Tags: django, pusher, javascript
Published: 2018-01-09 17:53:24

---
title: How to build a photo feed using Django and Pusher
published: true
description: How to build a photo feed using Django and Pusher
tags: Django, Pusher, Javascript
---

![](https://blog.pusher.com/wp-content/uploads/2017/06/build-a-photo-feed-using-django-and-pusher.gif)

Today, we will make a real-time photo feed framework utilizing Django and Pusher. This is like a mini Instagram, but without the comments and filter functionality.  
A basic understanding of Django and jQuery is needed to follow this tutorial.

This post was originally published on Pusher's blog [here](https://blog.pusher.com/build-a-photo-feed-using-django/)


## Setting up Django

First, we need to install the Django library if we don’t already have it.  
To install Django, we run:

```bash
pip install django
```

After installing Django, it’s time to create our project.  
Open up a terminal and create a new project using the following command:

```bash
django-admin startproject photofeed
```

In the above command, we created a new project called `photofeed`. The next step will be to create an app inside our new project. To do that, let’s run the following commands:

```bash
//change directory into the pusher_message directory 
cd photofeed 
//create a new app where all our logic would live 
django-admin startapp feed
```

Once we’re done setting up the new app, Django needs to know about our new application.  
To do this, we will go into our `feed\settings.py` and add the message app to our installed apps as seen below:

```python
INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'feed'
    ]
```

After doing the above, it’s time to run the application and see if all went well.  
In our terminal shell, we run:

```bash
python manage.py runserver
```

If we navigate our browser to `http://localhost:8000`, we should see the following:

![](https://blog.pusher.com/wp-content/uploads/2017/06/build-a-photo-feed-using-django-1.png)

## Set up an App on Pusher

At this point, Django is ready and set up. We need to set up Pusher next, as well as grab our app credentials.  
If you haven’t already, sign up to a free [Pusher](https://pusher.com/signup) account and create a new app, then copy your secret, application key and application id.

![](https://blog.pusher.com/wp-content/uploads/2017/06/build-a-photo-feed-using-django-2.png)

The next step is to install the required libraries:

```bash
pip install pusher
```

In the above bash command, we installed one package, Pusher.  
– Pusher: This is the official Pusher library for Python. We will be using this library to trigger and send our messages to the Pusher HTTP API

## Creating Our Application

First, let us create a model class, which will generate our database structure.  
Let’s open up `feed\models.py` and replace with the following:

```python
    from django.db import models

    # Create your models here.
    class Feed(models.Model):
        description = models.CharField(max_length=255, blank=True)
        document = models.FileField(upload_to='static/documents/')
```

In the above block of code, we defined a model called `Feed`. The Feed table will consist of the following fields:

- A field to store the description of the photo
- A field to store the photo

In the above code, while declaring our document field, we have included an `upload_to` attribute, which we set to `static/documents`. Please note that this path is relative to the path of the `DJANGO MEDIA ROOT`, which we will set now.  
While in this article, we will be setting the `MEDIA_ROOT` to the static folder in our `feed` app, so it can get served as a static file. To do that, let us move to our `photofeed/settings.py` and add the code below to our file, immediately after the `STATIC_URL` declaration.

```python
MEDIA_ROOT = os.path.join(BASE_DIR, 'feed/')
```

### Running Migrations

We need to make migrations and run them, so our database table can get created. To do that, let us run the following in our terminal:

```bash
python manage.py makemigrations 
python manage.py migrate
```

### Creating Our Views

Our views refer to the file/files that hold up the logic behind the application, often referred to as the `Controller`.  
Let us open up our `views.py` in our `feed` folder and replace with the following:

```python
    from django.shortcuts import render, HttpResponse
    from django.http import JsonResponse
    from .forms import *
    from pusher import Pusher
    import json

    #instantiate pusher
    pusher = Pusher(app_id=u'XXX_APP_ID', key=u'XXX_APP_KEY', secret=u'XXX_APP_SECRET', cluster=u'XXX_APP_CLUSTER')
    # Create your views here.
    # function that serves the welcome page
    def index(request):
        # get all current photos ordered by the latest
        all_documents = Feed.objects.all().order_by('-id')
        # return the index.html template, passing in all the feeds
        return render(request, 'index.html', {'all_documents': all_documents})

    #function that authenticates the private channel 
    def pusher_authentication(request):
        channel = request.GET.get('channel_name', None)
        socket_id = request.GET.get('socket_id', None)
        auth = pusher.authenticate(
          channel = channel,
          socket_id = socket_id
        )

        return JsonResponse(json.dumps(auth), safe=False)
    #function that triggers the pusher request
    def push_feed(request):
        # check if the method is post
        if request.method == 'POST':
            # try form validation
            form = DocumentForm(request.POST, request.FILES)
            if form.is_valid():
                f = form.save()
                # trigger a pusher request after saving the new feed element 
                pusher.trigger(u'a_channel', u'an_event', {u'description': f.description, u'document': f.document.url})
                return HttpResponse('ok')
            else:
                # return a form not valid error
                return HttpResponse('form not valid')
        else:
           # return error, type isnt post
           return HttpResponse('error, please try again')
```

In the code above, we have defined two main functions which are:

- index
- pusher\_authentication\_
- push\_feed

In the `index` function, we fetch all the available photos in the database. The photos are then rendered in the view. This enables a new user to see all previous feeds that are available.  
In the `pusher_authentication` function, we verify that the current user can access our private channel.  
In the `push_feed` function, we check if it is a POST request, then we try validating our form before saving it into the database. (The form used in this method named `DocumentForm` is not available yet. We will be creating it soon.) After the form validation, we then place our call to the Pusher library for real-time interaction.

### Creating The Form Class

A Django Form handles taking user input, validating it, and turning it into Python objects. They also have some handy rendering methods.  
Let us create a file called `forms.py` in our `feed` folder and add the following content to it:

```python
    from django import forms
    from .models import Feed

    class DocumentForm(forms.ModelForm):
        class Meta:
            model = Feed
            fields = ('description', 'document', )
```

In the above code block, we have imported our Feed model and used it to create a form. This form will now handle the validation and upload of images to the right folder.

### Populating The URL’s.py

Let us open up our `photofeed\urls.py` file and replace with the following:

```python
    """photofeed URL Configuration

    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/1.11/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.conf.urls import url, include
        2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
    """
    from django.conf.urls import url
    from django.contrib import admin
    from feed.views import * 

    urlpatterns = [
        url(r'^$', index),
        url(r'^push_feed$', push_feed),
        url(r'^pusher_authentication', pusher_authentication),
        url(r'^admin/', admin.site.urls),
    ]
```

What has changed in this file? We have added 2 new routes to the file.  
We have defined the entry point, and have assigned it to our `index` function. We also defined the push\_feed URL and assigned it to our `push_feed` function. This will be responsible for pushing updates to Pusher in real-time. Finally, the `pusher_authentication` endpoint, which handles the authentication of our private channel.

### Creating the HTML Files

Now we need to create the index.html file which we have referenced as the template for our index function.

 

Let us create a new folder in our `feed` folder called `templates`.  
Next, we create a file called `index.html` in our `templates` folder and replace it with the code below:

```html
    <html>
        <head>
            <title>Django Photo feed</title>
            <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
            <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
            <script src="//js.pusher.com/4.0/pusher.min.js"></script>
        </head>
        <body>

            <div class="container">
                <form  method="post" enctype="multipart/form-data" action="/push_feed" onsubmit="return feed_it()">
                <input type="hidden" id="csrf" name="csrf" value="{{ csrf_token }}"/>
                <div class="form-group">
                        <label for="usr">Image:</label>
                        <input type="file" id="document" name="document" class="form-control"  required>
                    </div>
                    <div class="form-group">
                        <label for="pwd">comment:</label>
                        <input type="text" id="description" name="description"  class="form-control"  required>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-success">Feed it</button>
                    </div>
                </form>
                <div class="row" id="feeds">
                    {% for doc in all_documents  %}
                    <span>
                        <h2>{{doc.description}}</h2>
                        <img  src="{{doc.document}}">
                    </span>
                    {% endfor %}
                </div>
            </div>
        </body>
    </html>

```

In this HTML snippet, note that we have included some required libraries such as:

- Bootstrap CSS
- jQuery JavaScript library
- Pusher JavaScript library

### Pusher Bindings And jQuery Snippet

That’s it! Now, once a photo gets uploaded, it also gets broadcast and we can listen using our channel to update the feed in real-time.  
Below is our example jQuery snippet used to handle the file upload as well as Pusher’s real-time updates.

```html
    <script>
     var files;

        // Add events
        $(document).ready(function() {
            $('input[type=file]').on('change', prepareUpload);
        })


        // Grab the files and set them to our variable
        function prepareUpload(event) {
            files = event.target.files;
        }

        function feed_it() {
            var data = new FormData();
            $.each(files, function(key, value) {
                data.append('document', value);
            });
            data.append('description', document.getElementById('description').value);

            data.append('csrfmiddlewaretoken', document.getElementById('csrf').value);
            $.post({
                url: '/push_feed',
                data: data,
                processData: false, // Don't process the files
                contentType: false, // Set content type to false as jQuery will tell the server it's a query string request
                success: function(data) {
                    if (data == "ok") {
                        alert('done');
                        document.getElementById('description').value = '';
                    }
                },
                error: function(error) {
                    alert('an error occured, please try again later')
                }
            });
            return false;
        }
        var pusher = new Pusher('XXX_APP_KEY', {
            encrypted: true,
            cluster: 'XXX_APP_CLUSTER',
            authTransport: 'jsonp',
            authEndpoint: '/pusher_authentication'
        });
        my_channel.bind("an_event", function(doc) {
            alert("message");
            var new_message = `<span>
                        <h2>` + doc.description + `</h2>
                        <img  src="` + doc.document + `">
                    </span>`;
            $('#feeds').prepend(new_message);
        });
    </script>
```

Below is an image of what we have built:

![](https://blog.pusher.com/wp-content/uploads/2017/06/build-a-photo-feed-using-django-and-pusher.gif)

## Conclusion

In this article, we have covered how to create a real-time photo feed using Django and Pusher as well as passing CSRF tokens in AJAX request using Django.  
The code base to this tutorial is available in a [public Github repository](https://github.com/samuelayo/pusher_django_photo_feed). You can download it for educational purposes.  
Have a better way we could have built our application, reservations or comments, let us know in the comments. Remember sharing is learning.