Skip to content

Commit

Permalink
Merge pull request #17 from agiliq/chapter_5_wiki
Browse files Browse the repository at this point in the history
final commit for chapter5
  • Loading branch information
yashrastogi16 committed Apr 3, 2018
2 parents dbd4ee0 + b2c88a3 commit 8cccee8
Show file tree
Hide file tree
Showing 66 changed files with 239 additions and 1,279 deletions.
12 changes: 6 additions & 6 deletions apps/djen_project/wiki/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from django.urls import re_path, include
from django.urls import path, include
from .views import add_article, edit_article, article_history, ArticleList, ArticleDetail

urlpatterns = [
re_path(r'^$', ArticleList.as_view(), name='wiki_article_index'),
re_path(r'^article/(?P<slug>[-\w]+)$',ArticleDetail.as_view(),name='wiki_article_detail'),
re_path(r'^history/(?P<slug>[-\w]+)$', article_history, name='wiki_article_history'),
re_path(r'^add/article$', add_article, name='wiki_article_add'),
re_path(r'^edit/article/(?P<slug>[-\w]+)$', edit_article, name='wiki_article_edit'),
path('', ArticleList.as_view(), name='wiki_article_index'),
path('article/<str:slug>',ArticleDetail.as_view(),name='wiki_article_detail'),
path('history/<str:slug>', article_history, name='wiki_article_history'),
path('add/article', add_article, name='wiki_article_add'),
path('edit/article/<str:slug>', edit_article, name='wiki_article_edit'),
]
246 changes: 233 additions & 13 deletions source/chapter5.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,61 @@ To demonstrate custom model managers, we would like to show only 'published' art

Let's write down the models:

.. literalinclude:: code/models_5db3e5f.py
.. sourcecode:: python

from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify


class PublishedArticlesManager(models.Manager):
def get_query_set(self):
return super(PublishedArticlesManager, self).get_query_set().filter(is_published=True)


class Article(models.Model):
"""Represents a wiki article"""

title = models.CharField(max_length=100)
slug = models.SlugField(max_length=50, unique=True)
text = models.TextField(help_text="Formatted using ReST")
author = models.ForeignKey(User, on_delete=models.CASCADE)
is_published = models.BooleanField(default=False, verbose_name="Publish?")
created_on = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
published = PublishedArticlesManager()

def __unicode__(self):
return self.title

def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Article, self).save(*args, **kwargs)
@models.permalink
def get_absolute_url(self):
return ('wiki_article_detail', (), {'slug': self.slug})


class Edit(models.Model):
"""Stores an edit session"""

article = models.ForeignKey(Article, on_delete=models.CASCADE)
editor = models.ForeignKey(User, on_delete=models.CASCADE)
edited_on = models.DateTimeField(auto_now_add=True)
summary = models.CharField(max_length=100)

class Meta:
ordering = ['-edited_on']

def __unicode__(self):
return "%s - %s - %s" % (self.summary, self.editor, self.edited_on)

@models.permalink
def get_absolute_url(self):
return ('wiki_edit_detail', self.id)


Most of the code should be familiar, some things that are new:

Expand All @@ -74,15 +128,28 @@ Most of the code should be familiar, some things that are new:

Now, we will need urls similar to our previous app, plus we would need a url to see the article history.

.. literalinclude:: code/urls_5db3e5f.py
.. sourcecode:: python

from django.urls import path, include
from .views import add_article, edit_article, article_history, ArticleList, ArticleDetail

urlpatterns = [
path('', ArticleList.as_view(), name='wiki_article_index'),
path('article/<str:slug>',ArticleDetail.as_view(),name='wiki_article_detail'),
path('history/<str:slug>', article_history, name='wiki_article_history'),
path('add/article', add_article, name='wiki_article_add'),
path('edit/article/<str:slug>', edit_article, name='wiki_article_edit'),
]

Note that:

* We will use the ``DetailView`` generic views for the article index page and detail page.

* Similarly, it would be better to write down custom views for edit article and article history pages.

Here are the forms we will need: ::
Here are the forms we will need:

.. sourcecode:: python

from django import forms

Expand All @@ -100,7 +167,6 @@ Here are the forms we will need: ::
fields = ['summary']



Here:

* We are excluding ``author`` and ``slug`` which will be autofilled.
Expand All @@ -110,7 +176,61 @@ Here:

In our custom views:

.. literalinclude:: code/views_201831.py
.. sourcecode:: python

from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.shortcuts import redirect, render, get_object_or_404
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from django.http import HttpResponse


from .models import Article, Edit
from .forms import ArticleForm, EditForm

@login_required
def add_article(request):
form = ArticleForm(request.POST or None)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
msg = "Article saved successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
return render(request, 'wiki/article_form.html', { 'form': form })

@login_required
def edit_article(request, slug):
article = get_object_or_404(Article, slug=slug)
form = ArticleForm(request.POST or None, instance=article)
edit_form = EditForm(request.POST or None)
if form.is_valid():
article = form.save()
if edit_form.is_valid():
edit = edit_form.save(commit=False)
edit.article = article
edit.editor = request.user
edit.save()
msg = "Article updated successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
return render(request, 'wiki/article_form.html',{'form': form, 'edit_form': edit_form, 'article': article})

def article_history(request, slug):
article = get_object_or_404(Article, slug=slug)
queryset = Edit.objects.filter(article__slug=slug)
return render(request, 'wiki/edit_list.html',{'article': article, 'queryset': queryset})

class ArticleList(ListView):
template_name = "wiki/article_list.html"
def get_queryset(self):
return Article.objects.all()

class ArticleDetail(DetailView):
model = Article
template_name = "wiki/article_detail.html"

* We are using the ``login_required`` decorator to only allow logged-in users to add/edit articles in our case logged in administrator.

Expand All @@ -136,23 +256,93 @@ To display all the articles on the index page:

``wiki/templates/wiki/article_list.html``:

.. literalinclude:: code/article_list_39527ee.html
:language: django
.. sourcecode:: html

{% if object_list %}

<h2>Recent Articles</h2>

<ul>
{% for article in object_list %}
<li>
<a href="{% url 'wiki_article_detail' article.slug %}">{{ article.title }}</a>
</li>
{% endfor %}
</ul>

{% else %}
<h2>No articles have been published yet.</h2>
{% endif %}

<a href="{% url 'wiki_article_add' %}">Create new article</a>

.. image:: images/wikilists.png

We will include links to edit and view history in the article detail page:

``wiki/templates/wiki/article_detail.html``:

.. literalinclude:: code/article_detail_5db3e5f.html
:language: django
.. sourcecode:: html

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

{% if not object.is_published %}
<label>Note: This article has not been published yet</label>
{% endif %}

<h2>{{ object.title }}</h2>

<p>
{{ object.text|restructuredtext }}
</p>

<h3>Actions<h3>
<ul>
<li>
<a href="{% url 'wiki_article_edit' object.slug %}">Edit this article</a>
</li>
<li>
<a href="{% url 'wiki_article_history' object.slug %}">View article history</a>
</li>
</ul>

<a href="{% url wiki_article_index %}">See All</a>

.. image:: images/wikidetail.png


Here's the form that would be used to create/edit an article:

``wiki/templates/wiki/article_form.html``

.. literalinclude:: code/article_form_39527ee.html
:language: django
.. sourcecode:: html

{% if article %}
<h1>Edit article {{ article }}</h1>
{% else %}
<h1>Create new article</h1>
{% endif %}

<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
{{ edit_form.as_table }}
</table>
<input type="submit" name="submit" value="Submit">
</form>

.. image:: images/wikiform.png

Note that the same form is used for add article and edit article pages. We pass the ``article`` context variable from edit page, so
we can use it to identify if this is an add or edit page. We also render the ``edit_form`` passed from edit page. Rendering an undefined
Expand All @@ -162,8 +352,38 @@ The article history template:

``wiki/templates/wiki/edit_list.html``

.. literalinclude:: code/edit_list_39527ee.html
:language: django
.. sourcecode:: html

<h2>History</h2>

<h3>{{ article }}</h3>

<table border="1" cellspacing="0">
<thead>
<th>Edited</th>
<th>User</th>
<th>Summary</th>
</thead>
<tbody>
{% for edit in object_list %}
<tr>
<td>{{ edit.edited_on }}</td>
<td>{{ edit.editor }}</td>
<td>{{ edit.summary }}</td>
</tr>
{% endfor %}
<tr>
<td>{{ article.created_on }}</td>
<td>{{ article.author }}</td>
<td>New article created</td>
</tr>
</tbody>
</table>

<br />
<a href="{% url 'wiki_article_detail' article.slug %}"><< Back</a>


Displays a table with the history.

.. image:: images/wikihistory.png
33 changes: 0 additions & 33 deletions source/code/article_detail_5db3e5f.html

This file was deleted.

15 changes: 0 additions & 15 deletions source/code/article_form_39527ee.html

This file was deleted.

17 changes: 0 additions & 17 deletions source/code/article_list_39527ee.html

This file was deleted.

0 comments on commit 8cccee8

Please sign in to comment.