A little Hacker News in Django (Part 1)

Daniel Dương
4 min readJun 3, 2018

--

It’s been a few months since I’ve worked on a Django project, I wanted a little project to get back to it. So here I am writing a little Hacker News.

Like with any project, let’s start by creating a git repo.

# daniel @ Evans in ~/Code/hnews [17:42:08]
$ git init .
Initialized empty Git repository in /Users/daniel/Code/hnews/.git/

Let’s be cool and use pipenv for the environment. I used to just write a requirements.txt and create a virtual environment with virtualenvwrapper. But, pipenv seems to be the tool to use now.

# daniel @ Evans in ~/Code/hnews on git:master x [17:45:28] C:1
$ pipenv install django

Now, to work on the environment, instead of doing workon myenv or source path_to_my_env/bin/activate I just do this:

# daniel @ Evans in ~/Code/hnews on git:master x [17:45:55]
$ pipenv shell

Now, let’s create a project with Django.

# daniel @ Evans in ~/Code/hnews on git:master x [17:46:56]
$ django-admin startproject hnews .

I think now that it’s time to create our first commit. But first, let’s grab Github’s gitignore.

# daniel @ Evans in ~/Code/hnews on git:master x [17:47:56]
$ curl https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore > .gitignore

Ok now, let’s do our first commit.

# daniel @ Evans in ~/Code/hnews on git:master x [17:49:21]
$ git add .
# daniel @ Evans in ~/Code/hnews on git:master x [17:49:23] C:1
$ git commit

Since my second job, I’ve stopped using git commit -m “some message”. With git commit, I can write a long commit message with more context and explanations about the changes. But well, with this first commit, there isn’t much to say.

Ok now let’s start our first app. It’s always hard for me to know what to put in an app, how to name it, etc. It’s even harder with such a little project where you could basically put everything in the same app. You can think in terms of urls like “ok, everything that is after /articles/ is going to be in the articles app”. Also, depending on where you work, sometimes the app has an “s” or no “s” in the end. For example, some people would name the app “article”, other people “articles”.

Anyway, let’s just create a “posts” app.

# daniel @ Evans in ~/Code/hnews on git:master o [17:51:52]
$ ./manage.py startapp posts

# daniel @ Evans in ~/Code/hnews on git:master x [17:56:11]
$ mv posts hnews

In terms of editors, I don’t really have a favorite tool. I usually change between vim, Sublime Text and PyCharm. But for Django projects, I usually like to use PyCharm. Lately I’ve been trying the vim plugin on PyCharm, it’s not bad.

Now I need to modify the name of the app, otherwise Django will complain. I have to replace ‘posts’ by ‘hnews.posts’.

# daniel @ Evans in ~/Code/hnews on git:master x [17:57:04]
$ cat hnews/posts/apps.py
from django.apps import AppConfig
class PostsConfig(AppConfig):
name = 'hnews.posts'

Now I have to add my app in the settings. So I open hnews/settings.py and I add my app in INSTALLED_APPS.

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'hnews.posts.apps.PostsConfig',
]

People have different ways to organize the INSTALLED_APPS. You can separate in different lists like: DJANGO_APPS, THIRD_PARTY, LOCAL_APPS. In the end you would do something like this:

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY + LOCAL_APPS

Anyway, for such a little project, I’m fine with putting everything in INSTALLED_APPS. As they say: YAGNI.

Ok, let’s write our Post model. So, looking at Hacker News, we have:

  • A creator
  • A creation_date
  • A url
  • The ability to hide a post
  • Comments
  • Points

I’m kind of lazy to do the hiding a post feature, since I need to create an account to see how it works. Ok now, let’s do the Post. Ok first, the creator.

from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
creator = models.ForeignKey(User, related_name='posts')

I usually like to put a ‘related_name’, it allows me to do things like this:

my_user.posts.all()

Now the creation date.

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
creator = models.ForeignKey(User, related_name='posts')
creation_date = models.DateTimeField(auto_now_add=True)

With ‘auto_now_add’, Django sets the creation_date automatically when the object is created. Now for the url:

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
creator = models.ForeignKey(User, related_name='posts')
creation_date = models.DateTimeField(auto_now_add=True)
url = models.URLField()

Ok, maybe it’s time to commit.

Actually, looking at the documentation, I just thought about a problem: What happens when a User is deleted? First I thought to put on_delete=models.CASCADE, but that would delete the Post when the user is deleted. We want to keep the post, but set the creator to NULL. So, let’s fix that.

class Post(models.Model):
creator = models.ForeignKey(
User,
related_name='posts',
on_delete=models.SET_NULL,
null=True,
)
creation_date = models.DateTimeField(auto_now_add=True)
url = models.URLField()

Now the creator can be NULL, it means that the creator doesn’t exist anymore. Let’s commit this change.

Now, I just realized that I didn’t put .idea in my .gitignore, so I have a bunch of useless diffs (don’t forget it!). Apparently, some of the things can be put on git, but well, I’m too lazy to see what is useful. Now I have to clean all that stuff.

mv .idea .idea2
git add .idea .gitignore
git commit
mv .idea2 .idea

Well, I think it’s time for me to have dinner, so I guess the rest will be for later :).

Some links you might be interested in:

See ya!

--

--