Building Djangobin – The First Steps

In the previous few chapters, we have learned quite a lot about Django. In this chapter, we will take the first steps to build our djangobin application.

Creating Snippets

Let’s start by building a form which will allow users to submit new snippets.

Open djangobin app’s forms.py file. At this point, it should look like this:

djangobin/django_project/djangobin/forms.py

Delete everything and enter the following code:

djangobin/django_project/djangobin/forms.py

Here we are defining a SnippetForm class which inherits from forms.ModelForm. The model attribute of the Meta class connects SnippetForm to the Snippet model and fields attribute specifies a list of model fields that you want to show in the form.

In line 12, we are using widgets attribute of the inner Meta class to add bootstrap CSS classes and other attributes to the model fields.

In lines 24-29, we are overriding the save() method of the ModelForm class. The save() method takes request as an argument so that the logged in user can be accessed inside the method. In line 26, we are calling the parent class’s save() method with commit=True. By default, the ModelForm‘s save() method creates an instance of the model that the form is connected to, saves it to the database and returns it. If you call save() method with commit=True then it only creates and returns the model instance without saving it to the database. We usually do this when we want to modify the object before saving or set some additional data, which is what we do next.

In line 27, we are calling get_current_user() function with the request argument. The get_current_user() is a utility function defined in djangobin app’s utils.py file as follows:

djangobin/django_project/djangobin/utils.py

A Snippet model has a one-to-many relationship with the User model. As a result, a Snippet object must be associated with a User object. If a user is creating snippet after logging in then we want to assign the snippet to that user. Otherwise, we want to assign the snippet to a guest user. This is essentially what get_current_user() function does. If the user is logged in then get_current_user() returns an instance of that user. Otherwise, it returns an instance of a guest user which is just a User object whose username is guest.

Once the user is set, we save the Snippet object and return it.

Start the Django shell and create a new guest user as follows:

Next, open views.py and add modify index() view function at the top of the file as follows:

djangobin/django_project/djangobin/views.py

This view function displays the SnippetForm form and saves the submitted snippet to the database.

Before, we move on to the next section remove add_lang and update_lang URL patterns and view functions associated with it.

A Base Template For Djangobin

Next, let’s set up a base template for djangobin app. Create a template named base.html in templates/ directory of the djangobin app with the following code:

djangobin/django_project/djangobin/templates/djangobin/base.html

Most of the code should be straightforward. But we will still go through it just to make sure you understand everything.

Recall that inside the template you always have access to the request variable. We use this variable to access the details of the current web request and the logged in user.

In line 5, we load {% static %} tag using the {% load %} tag.

In line 10, we define a block named title. The templates which inherit from this template can fill the block with content.

In lines 27-28, we use {% static %} tag to build URLs for the CSS files.

In lines 50-61, we use several {% if %} tags to add a class of active to the corresponding <li> element to highlight the current option in the menu.

In lines 73-77, we test whether the user is logged in. If so, we display logged in username after applying the upper filter. If the user is not logged in, we display GUEST.

In lines 80-93, we again test whether the user logged in. If so, we display some profile related links like My Pastes, Settings, Logout and so on. Otherwise, we display links to Login and Sign Up page.

In lines 107-111, we display a link to the login page, only if the user is not logged and request.path is not equal to /login/.

In line 114, we define a block named main. The content for this block will be provided by the child template.

In lines 214-221, we again use {% static %} tag to build links to the JavaScript files.

As you might have noticed, the href attribute of the most <a> element is empty. We will update them as we move along.

Now, we create a child template named index.html in the templates directory
with the following code:

djangobin/django_project/djangobin/templates/djangobin/index.html

There is nothing new in this template that deserves an explanation. We are merely using things we have learned up until now. In case you need a refresher check out Basics of Django templates chapter.

If you now visit http://localhost:8000/. You will see a page like this:

The page appears to be rendering correctly but there is one problem.

Take a closer look at how Tags field is rendered:

Since Snippet model has a many-to-many relationship with Tag model, the tags are displayed using multiple selection <select> box. This means that you can only select tags which already exists in the database.

It is impossible to anticipate all the tags that users will be using to create snippets. As a result, a better approach would be to let users specify a comma-separated list of tags while creating a snippet. To do that we have to define a new form field in the SnippetForm class.

In the forms.py, define snippet_tag field above the Meta class as follows:

djangobin/django_project/djangobin/forms.py

Also, remove the tags field from the fields attribute of the Meta class.

djangobin/django_project/djangobin/forms.py

To incorporate the changes we now have to update the save() method of the SnippetForm class.

djangobin/django_project/djangobin/forms.py

In line 14, we are using list comprehension along with cleaned_data attribute of the form object to create a list of submitted tags.

If tag_list is not empty, we loop over it using a for loop. Inside the for loop, we create Tag object (only if it doesn’t already exist) and associate it with the Snippet object.

It is important to note that snippet_tags field of SnippetForm is a completely new field and it is not related to tags field of the Snippet model in any way.

At last, update index.html to use snippets_tags field as follows:

djangobin/django_project/djangobin/templates/djangobin/index.html

We can now specify comma separated list of tags while creating snippets. In the next section, we will create the page to display the highlighted snippet.

Displaying Snippets

In the views.py file, modify snippet_detail() view , just below the index() view function as follows:

djangobin/django_project/djangobin/views.py

This view function displays the highlighted snippet and also increments the hits count by 1.

Create a new template named snippet_detail.html in the templates directory with the following code:

djangobin/django_project/djangobin/templates/djangobin/snippet_detail.html

In lines 13-31, we display metadata of the snippet and in line 44, we display the highlighted code.

Now visit http://localhost:8000/ and try creating a snippet. You will get the snippet detail page like this:

Downloading Snippets

Just below snippet_detail() view, define download_snippet() view function as follows:

This view function retrieves the snippet from the database, sends it to the client.

Note that we are setting an additional header named Content-Disposition just after the creation of HttpResponse instance. The Content-Disposition header tells the browser to save the response as an attachment instead of displaying it.

Next, open djangobin app’s urls.py and add a new URL pattern named download_snippet as follows:

djangobin/django_project/djangobin/urls.py

Now, to let users download code, we have to add a link in the snippet details page. Open snippet_detail.html and modify the <div> tag with class="codeblock" as follows:

djangobin/django_project/djangobin/templates/djangobin/snippet_detail.html

Visit http://localhost:8000/ and create a new snippet. In the snippet detail page click the “download” link to download the snippet.

Displaying Raw Snippets

In the views.py, add raw_snippet() view function below the download_snippet() view as follows:

djangobin/django_project/djangobin/views.py

This view will display the snippet in raw format.

Add a new URL pattern named raw_snippet to urls.py as follows:

djangobin/django_project/djangobin/utils.py

Finally, add a link to raw snippet in snippet_detail.html template as follows:

You can now view the raw snippet by clicking the “raw” link in the Snippet detail page.

Displaying Recent Snippets using Context Processor

We want to display recent public snippets on every page of our Djangobin application. Currently, recent Snippet list is just a series of hardcoded <a> tags.

At first, you might think that we can easily display recent snippets by doing something like this:

djangobin/django_project/djangobin/views.py

There is nothing wrong with this approach, the only problem is that if we choose to go by this route then we would have to repeatedly fetch recent snippet list and pass it to the respective template inside every view function – Context Processor to the rescue.

In lesson Loading Templates, we have learned that render() function automatically makes certain variables available inside all the templates. Two such variables are request and message. The request variable which contains data about the current request and messages variable contains the flash messages. The render() function does this using something called RequestContext.

The RequestContext is just a special subclass of Context. The RequestContext differs from Context in the following ways:

  1. It accepts request as its first argument.
  2. It automatically populates the template context, based on the context_processors option.

The context_processors is just list of callables, called context processor. Each context processor is a function which accepts a HttpRequest object and returns a dictionary of items to be merged into the template context. By default context_processors list looks like this:

djangobin/django_project/django_project/settings.py

The source of request and message callables looks like this:

Now you know from where the request and messages variables come from.

To make a variable globally accessible to all your templates, you’ll have to define a custom context processor.

Create a new file named context_processors.py inside the djangobin app directory and add the following code to it:

djangobin/django_project/djangobin/context_processors.py

Add this context processor to context_processors option in settings.py file as follows:

djangobin/django_project/django_project/settings.py

Now, all of our templates have access to recent_snippets variable.

To display recent snippets we have to make some changes in djangobin application’s base.html file. Open base.html and modify it as follows:

djangobin/django_project/djangobin/templates/djangobin/base.html

Visit home or snippets detail page and you will see recent snippets list like this:

Humanizing Time

Currently, recent snippet list displays date and time in the following format:

A more user-friendly way to would be to display date and time like this:

  1. 2 weeks ago
  2. 23 hours ago
  3. 10 seconds ago

and so on.

Django comes with a built-in app called humanize which provides template filters to format numbers, date and time.

The humanize app is not installed by default. To install it add
django.contrib.humanize to the INSTALLED_APPS list in settings.py file as follows:

djangobin/django_project/django_project/settings.py

Now open base.html template and add {% load humanize %} just below the line where you are loading the static template tag:

djangobin/django_project/djangobin/templates/djangobin/base.html

At last, add naturaltime filter to base.html as follows:

djangobin/django_project/djangobin/templates/djangobin/base.html

Visit home or snippet detail page and you will see the updated date and time format as follows:

2 thoughts on “Building Djangobin – The First Steps

  1. I am unable to post a snippet with the above code. Please provide GitHub code or further documentation in order for this to work.

Leave a Comment