Skip to content

Instantly share code, notes, and snippets.

@eclecticmiraclecat
Created June 13, 2021 13:14
Show Gist options
  • Save eclecticmiraclecat/e0907fe360ae8942f8fad37d9b7cf90e to your computer and use it in GitHub Desktop.
Save eclecticmiraclecat/e0907fe360ae8942f8fad37d9b7cf90e to your computer and use it in GitHub Desktop.

Chapter 3. Building a Pastebin

In this chapter we will be designing a simple pastebin. Our pastebin will be able to

  • Allow users to paste some text
  • Allow users to edit or delete the text
  • Allow users to view all texts
  • Clean up texts older than a day

Some ‘views’ that the user will see are

  • A list view of all recent texts
  • A detail view of any selected text
  • An entry/edit form for a text
  • A view to delete a text

Our work flow for this app would be

  • sketch the models
  • route urls to generic views
  • use generic views with our models
  • write the templates to use generic views

Starting a django project

$ mkdir chapter3 && cd chapter3
$ django-admin.py startproject chap3 .

Create app

$ python manage.py startapp pastebin

Add app to settings.py

# chap3/settings.py

INSTALLED_APPS = [
    ...
    'pastebin.apps.PastebinConfig',
]

Models

# pastebin/models.py

from django.db import models
from django.urls import reverse

# Create your models here.
class Paste(models.Model):
    text = models.TextField()
    name = models.CharField(max_length=40, null=True, blank=True)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name or str(self.id)

    def get_absolute_url(self):
        return reverse('pastebin_paste_detail', args=[str(self.id)])

Makemigrations and Migrate

$ python manage.py makemigrations
$ python manage.py migrate

URLs

# chap3/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('pastebin/', include('pastebin.urls')),
]
from django.urls import path
from .views import PasteCreate, PasteDetail, PasteList, PasteDelete, PasteUpdate

urlpatterns = [
    path(r'', PasteCreate.as_view(), name='pastebin_paste_create'),
    path('paste/<int:pk>', PasteDetail.as_view(), name='pastebin_paste_detail'),
    path('pastes/', PasteList.as_view(), name='pastebin_paste_list'),
    path('paste/delete/<int:pk>', PasteDelete.as_view(), name='pastebin_paste_delete'),
    path('paste/edit/<int:pk>', PasteUpdate.as_view(), name='pastebin_paste_edit'),
]

Views

# chap3/views.py

from django.urls import reverse_lazy
from django.views.generic import DeleteView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from .models import Paste

class PasteCreate(CreateView):
    model = Paste
    fields = ['text','name']

class PasteDetail(DetailView):
    model = Paste
    template_name = "pastebin/paste_detail.html"

class PasteList(ListView):
    model = Paste
    template_name = "pastebin/paste_list.html"
    queryset = Paste.objects.all()
    context_object_name = 'queryset'

class PasteDelete(DeleteView):
    model = Paste
    success_url = reverse_lazy('pastebin_paste_list')

class PasteUpdate(UpdateView):
    model = Paste
    fields = ['text', 'name']

Templates

<!-- templates/pastebin/paste_form.html -->

<h1>Create new Paste</h1>
<form action="" method="POST">
    {% csrf_token %}
    <table>
        {{ form.as_table }}
    </table>
    <input type="submit" name="create" value="Create">
</form>
<!-- templates/pastebin/paste_detail.html -->

{% if messages %}
    <div class="messages">
    <ul>
    {% for message in messages %}
        <li class="{{ message.tag }}">
            {{ message }}
        </li>
        {% endfor %}
    </ul>
    </div>
{% endif %}

<h1>Paste Details: </h1>
<p>
    <div>
        <label>ID</label>
        <span>{{ object.id }}</span>
    </div>
    <div>
        <label>Name</label>
        <span>{{ object.name }}</span>
    </div>
    <div>
        <label>Text</label>
        <textarea rows="10" cols="50" OnClick="this.select();" readonly="true">{{ object.text }}</textarea>
    </div>
    <div>
        <label>Created</label>
        <span>{{ object.created_on }}</span>
    </div>
    <div>
        <label>Modified</label>
        <span>{{ object.updated_on }}</span>
    </div>
</p>

<h2>Actions</h2>
    <ul>
        <li>
            <a href="{% url 'pastebin_paste_edit' object.id %}">Edit this paste</a>
        </li>
        <li>
            <a href="{% url 'pastebin_paste_delete' object.id %}">Delete this paste</a>
        </li>
    </ul>

<a href="{% url 'pastebin_paste_list' %}">View All</a>
<!-- templates/pastebin/paste_list.html -->

{% if messages %}
    <div class="messages">
    <ul>
    {% for message in messages %}
        <li class="{{ message.tag }}">
            {{ message }}
        </li>
        {% endfor %}
    </ul>
    </div>
{% endif %}


{% if object_list %}
    <h1>Recent Pastes:</h1>
<ul>
    {% for paste in object_list %}
    <li>
        <a href="{% url 'pastebin_paste_detail' paste.id  %}">{{ paste }}</a>
    </li>
    {% endfor %}
</ul>
{% else %}
    <h1>No recent pastes</h1>
{% endif %}

<a href="{% url 'pastebin_paste_create' %}">Create new</a>
<!-- templates/pastebin/paste_confirm_delete.html -->

<h1>Really delete paste {{ object }}?</h1>
<h2>This action cannot be undone</h2>
<form action="{% url 'pastebin_paste_delete' object.id %}" method="POST">
    {% csrf_token %}
    <input type="submit" value="Delete">
</form>

Writing custom management scripts

$ mkdir -p pastebin/management/commands
# pastebin/management/commands/delete_old.py

import datetime

from django.core.management.base import BaseCommand

from pastebin.models import Paste

class Command(BaseCommand):
    help = """
            deletes pastes not updated in last 24 hrs

            Use this subcommand in a cron job
            to clear older pastes
           """

    def handle(self, **options):
        now = datetime.datetime.now()
        yesterday = now - datetime.timedelta(1)
        old_pastes = Paste.objects.filter(updated_on__lte=yesterday)
        old_pastes.delete()
$ python manage.py delete_old
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment