Revisiting cadmin app

 In this lesson we will be building remaining pages of our cadmin app.  At this point our cadmin has following pages:

In the upcoming section, we are creating the following pages:

  1. Post list page - to display paginated list of posts.
  2. Tag list page - to display paginated list of tags,
  3. Tag add page.
  4. Tag update page.
  5. Tag delete view.
  6. Category list page - to display paginated list of categories.
  7. Category add page.
  8. Category update page.
  9. Category delete page.
  10. Account Info page - to display logged in user details

In addition to that we will also modify Post add/update page.

Let's start modifying Post Add page.

Modifying Post Add Page

As situation stands everytime we add or update a post we are presented with a author dropdown list, which is unneccessay. Instead of showing a author dropdown list, it would be much better if we automatically associate logged in author with the post and only show author dropdown list to site superusers's i.e users whose is_superuser attribute is True.

Further, It will not be mandatory for the logged in user (superuser) to select the author, in case he didn't select any author we will assign a special author account (staff) to the post. Okay, let's implement this:

Open post_add.html and modify the file as follows:

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Add New Post - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'post_list' %}">All Posts</a> &#187; Add Post</p>

        <h3>Add Post</h3>

        <P>
            {{ form.non_field_errors }}
        </P>

        <form action="{% url 'post_add' %}" method="post">
            {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.title.label_tag }}</td>
                    <td>
                        {{ form.title.errors }}
                        {{ form.title }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.content.label_tag }}</td>
                    <td>
                        {{ form.content.errors }}
                        {{ form.content }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.category.label_tag }}</td>
                    <td>
                        {{ form.category.errors }}
                        {{ form.category }}
                    </td>
                </tr>

                <tr>
                    <td>{{ form.tags.label_tag }}</td>
                    <td>
                        {{ form.tags.errors }}
                        {{ form.tags }}
                    </td>
                </tr>

                {#  Show authors only if logged in user is a superuser  #}
                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}

                <tr>
                    <td></td>
                    <td><input type="submit" value="Add Post"></td>
                </tr>
            </table>
        </form>

    </div>

{% endblock %}

Here we are rendering each form field manually. Notice that we are only displaying author list when the logged in author is superuser. So, essentially we want to make author field optional but the problem is our current state of the Post model doesn't allow that. To view this problem login to cadmin app using non superuser account and try creating a new post.

[img]

After hitting the save button you will be redirected to Post add page again. So what's going on ? The problem is that the form validation failed because we have not provided any value to the author field.

But! Where are the errors ?

We didn't get any error messages because we have hidden the author dropdown field. To view the error message, comment out if tag in post_add.html and hit the submit button again in Post add page.

...
{#  Show authors only if logged in user is a superuser  #}
{# {% if request.user.is_superuser %} #}
    <tr>
        <td>{{ form.author.label_tag }}</td>
        <td>
            {{ form.author.errors }}
            {{ form.author }}
        </td>
    </tr>
{# {% endif %} #}
...

[post_add_page_with_author_field_required_error]

One way to solve this problem is to create amend our PostForm class which resides in forms.py file in the blog app.

...
class PostForm(forms.ModelForm):    
    author = forms.ModelChoiceField(queryset=Author.objects.all(), required=False)

    class Meta:
        model = Post
        fields = ('title', 'content', 'author', 'category', 'tags',)

    ...

Here we are redefining author field as ModelChoiceField field. The ModelChoiceField will be rendered as dropdown list and will use data sepcified in the queryset parameter to populate the field. The required=False makes the author field optional.

Now we need a new special Author account to assign post in case a superuser not selected any author from the dropdown list.

Create a new Author by visiting http://127.0.0.1:1000/cadmin/register/ page. Hop back to Django shell and set it's is_superuser attribute to True.

[img]

>>>
>>> from blog.models import Author
>>>
>>> u = Author.objects.get(user__username='staff')
>>> u
<Author: staff>
>>>
>>> u.user
<User: staff>
>>>
>>> u.user.is_superuser
False
>>>
>>> u.user.is_superuser = True
>>>
>>> u.save()
>>>
>>> u.user.save()
>>>

Now the only thing remain is to update our post_add() view. Open post_add() view and modify it as follows:

blog/views.py

...
@login_required
def post_add(request):

    # If request is POST, create a bound form(form with data)
    if request.method == "POST":
        f = PostForm(request.POST)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the add post form

        # If form is invalid show form with errors again
        if f.is_valid():
            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser, then assign the
                # post to special author account
                new_post = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                new_post.author = author
                new_post.save()
                f.save_m2m()

            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                new_post = f.save()

            else:
                # if author not a superuser
                new_post = f.save(commit=False)
                print("new_post.tags")
                # print(new_post.tags)
                new_post.author = Author.objects.get(user__username=request.user.username)
                new_post.save()
                f.save_m2m()

            return redirect('post_add')

    # if request is GET the show unbound form to the user
    else:
        f = PostForm()

    return render(request, 'cadmin/post_add.html', {'form': f})
...

There is nothing new in the above code except commit=False and save_m2m() method. As you know calling save() method on a ModelForm class saves the object in the database. When we pass commit=False to the save() method, then the save() method just returns the object instead of saving it into the database. We do this usually when we want to add some additional data to the object; In this case we are assigning author object to the post.

A side effect of passing commit=True to save() method is that, if your model has many-to-many relation with other model, then Django will not save the data for many-to-many relation. To solve this issue Django provides a method called save_m2m() to every ModelForm class. Calling save_m2m() saves the data for many-to-many relation.

To view the fruits of our labour login to cadmin app using a superuser account and create a new post by visiting http://127.0.0.1:8000/cadmin/post/add/ page.

Enter the data in all the fields except author field and hit "Add Post" to create new post.

[]

This action will cause the execution of following snippet in the post_add() view:

...
if request.POST.get('author') == "" and request.user.is_superuser:
    # if author is not supplied and user is superuser
    new_post = f.save(commit=False)
    author = Author.objects.get(user__username='staff')
    new_post.author = author
    new_post.save()
    f.save_m2m()
...

Create another post but this time select an author from the dropdown list.

[]

This action will trigger the execution of elif block in post_add() view:

...
elif request.POST.get('author') and request.user.is_superuser:
    # if author is supplied and user is superuser
    new_post = f.save()
...

Finally, logout from cadmin app and login again using non superuser account. And create a new post again visiting http://127.0.0.1:1000/cadmin/post/add/ page.

[img]

As you can see the logged in user is not allowed to select author. Create a post and hit "Add Post" button. This action will execute else block of the code in the post_add() view:

...
 else:
    # if author not a superuser
    new_post = f.save(commit=False)
    print("new_post.tags")
    # print(new_post.tags)
    new_post.author = Author.objects.get(user__username=request.user.username)
    new_post.save()
    f.save_m2m()
...

Updating Post Update page

Open views.py and amend post_update() view as follows:

...
@login_required
def post_update(request, pk):
    post = get_object_or_404(Post, pk=pk)

    # If request is POST, create a bound form(form with data)
    if request.method == "POST":
        f = PostForm(request.POST, instance=post)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the update post form

        # If form is invalid show form with errors again
        if f.is_valid():
            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser
                updated_post = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                updated_post.author = author
                updated_post.save()
                f.save_m2m()
            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                updated_post = f.save()
            else:
                # if author not a superuser
                updated_post = f.save(commit=False)
                updated_post.author = Author.objects.get(user__username=request.user.username)
                updated_post.save()
                f.save_m2m()
            return redirect(reverse('post_update', args=[post.id]))

    # if request is GET the show unbound form to the user
    else:
        f = PostForm(instance=post)

    return render(request, 'cadmin/post_update.html', {'form': f, 'post': post})
...

Next, open post_update.html and modify the page as follows:

blog/templates/blog/post_update.html

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Update Post - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">
        <p>&#187; <a href="{% url 'post_list' %}">All Posts</a> &#187; Post Update</p>

        <h3>Post Update</h3>

        <P>
            {{ form.non_field_errors }}
        </P>

        <form action="{% url 'post_update' post.id %}" method="post">
            {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.title.label_tag }}</td>
                    <td>
                        {{ form.title.errors }}
                        {{ form.title }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.content.label_tag }}</td>
                    <td>
                        {{ form.content.errors }}
                        {{ form.content }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.category.label_tag }}</td>
                    <td>
                        {{ form.category.errors }}
                        {{ form.category }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.tags.label_tag }}</td>
                    <td>
                        {{ form.tags.errors }}
                        {{ form.tags }}
                    </td>
                </tr>
                {#  Show authors only if logged in user is a superuser  #}
                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}
                <tr>
                    <td></td>
                    <td><input type="submit" value="Update Post"></td>
                </tr>
            </table>
        </form>

    </div>

{% endblock %}

Now just like with Post Add page page Author dropdown list will only appear in Post Update page when the logged in user is superuser.

Creating Post List Page

Open cadmin's views.py and add post_list() view as follows:

...
from django.contrib.auth.decorators import login_required
from django_project.helpers import generate_activation_key, pg_records

@login_required
def post_list(request):
    if request.user.is_superuser:
        posts = Post.objects.order_by("-id").all()
    else:
        posts = Post.objects.filter(author__user__username=request.user.username).order_by("-id")
    
    posts = pg_records(request, posts, 5)

    return render(request, 'cadmin/post_list.html', {'posts': posts})
...

Next, create post_list.html page with the following code:

{% extends "cadmin/base_admin.html" %}

{% block title %}
    All Posts - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'post_list' %}">All Post</a> </p>

        <p class="button"><a href="{% url 'post_add' %}">Add Post &raquo;</a></p>

        <p class="count">Total Posts: {{ posts.paginator.count }}</p>
        
            {% for post in posts %}
                {% if forloop.first     %}
                    <table class="tbl-class">
                        <tr>
                            <th>Title</th>
                            <th>Category</th>
                            {% if request.user.is_superuser %}
                                <th>Author</th>
                            {% endif %}
                            <th>Date</th>
                            <th>Action</th>
                        </tr>
                {% endif %}

                <tr>
                    <td class="post-title" title="{{ post.title }}">{{ post.title|truncatechars:50 }}</td>
                    <td>{{ post.category }}</td>
                    {% if request.user.is_superuser %}
                        <td>{{ post.author }}</td>
                    {% endif %}

                    <td title="{{ post.pub_date }}">{{ post.pub_date|date:"d M Y" }}</td>
                    <td>
                        <a href="{% url 'post_update' post.id %}">Edit</a> |
                        <a href="{% url 'post_delete' post.id %}?next={{ request.get_full_path }}" onclick=" return confirm('Are you sure ?')">Delete</a>
                    </td>
                </tr>

                {% if forloop.last %}
                    </table>
                {% endif %}

            {% empty %}
                No Posts
            {% endfor %}

    </div>

    {% if posts %}

        <div class="pagination">
            <p>
                {% if posts.has_previous %}
                    <a href="?page={{ posts.previous_page_number }}">&lt; Prev</a> |
                {% endif %}

                {% if posts.has_next %}
                    <a href="?page={{ posts.next_page_number }}">Next &gt;</a>
                {% endif %}

                <span>Page {{ posts.number }} of {{ posts.paginator.num_pages }}</span>
            </p>
        </div>

    {% endif %}

{% endblock %}

We want users to show list of post at the root URL of cadmin app i.e http://127.0.0.1:8000/cadmin/. Currently root URL of cadmin app points to home() view function.

urlpatterns = [
    ...
    url(r'^$', views.home, name='home'),
    ...
]   

Update the above url pattern as follows:

urlpatterns = [
    ...
    url(r'^$', views.post_list, name='post_list'),
    ...
]   

If you try to view post list page (http://127.0.0.1:8000/cadmin/) at this point, you will get an error as follows:

[img]

This is because we haven't yet created any url pattern to delete post objects.

Deleting Posts

To allow deletion of post from cadmin add post_delete() view just after post_update() view as follows:

@login_required
def post_delete(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.delete()
    next_page = request.GET['next']

    return redirect(next_page)

Now open cadmin's urls.py and add the following url pattern to just below post_update url pattern.

urlpatterns = [
    ...
    url(r'^post/delete/(?P<pk>[\d]+)/$', views.post_delete, name='post_delete'),
    ...
]

To view changes in action visit http://127.0.0.1:8000/cadmin/ and you should see a link to delete post beside Edit link as follows:

[img]

Creating Category List Page

Open views.py and add category_list() view just below post_delete() view as follows:

@login_required
def category_list(request):
    if request.user.is_superuser:
        categories = Category.objects.order_by("-id").all()
    else:
        categories = Category.objects.filter(author__user__username=request.user.username).order_by("-id")

    categories = pg_records(request, categories, 5)

    return render(request, 'cadmin/category_list.html', {'categories': categories})

Create a new template called category_list.html with the following code:

{% extends "cadmin/base_admin.html" %}

{% block title %}
    All Posts - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'category_list' %}">All Categories</a></p>

        <p class="button"><a href="{% url 'category_add' %}">Add Category &raquo;</a></p>

        <p class="count">Total Categories: {{ categories.paginator.count }}</p>

            {% for category in categories %}
                {% if forloop.first     %}
                    <table class="tbl-class">
                        <tr>
                            <th>Name</th>
                            <th>Slug</th>
                            <th>Author</th>
                            <th>Action</th>
                        </tr>
                {% endif %}

                <tr>
                    <td class="post-title" title="{{ category.name }}">{{ category.name|truncatechars:50 }}</td>
                    <td>{{ category.slug }}</td>
                    <td>{{ category.author.user.username }}</td>
                    <td>
                        <a href="{% url 'category_update' category.id %}">Edit</a> |
                        <a href="{% url 'category_delete' category.id %}?next={{ request.get_full_path }}" onclick=" return confirm('Are you sure ?')">Delete</a>
                    </td>
                </tr>

                {% if forloop.last %}
                    </table>
                {% endif %}

            {% empty %}
                No Posts
            {% endfor %}

        </table>

    </div>

    {% if categories %}

        <div class="pagination">
            <p>
                {% if categories.has_previous %}
                    <a href="?page={{ categories.previous_page_number }}">&lt; Prev</a> |
                {% endif %}

                {% if categories.has_next %}
                    <a href="?page={{ categories.next_page_number }}">Next &gt;</a>
                {% endif %}

                <span>Page {{ categories.number }} of {{ categories.paginator.num_pages }}</span>
            </p>
        </div>

    {% endif %}

{% endblock %}


Next, add the following url pattern just below post_delete as follows:

urlpatterns = [
    ...
    url(r'^category/$', views.category_list, name='category_list'),
    ...
]

Creating Category Add/Update Page

Category Add/Update page will be similar to Post Add/Update page. Here too we will display author dropdown list only to the superusers.

First open forms.py and redefine author field in CategoryForm class as follows:

...
class CategoryForm(forms.ModelForm):
    author = forms.ModelChoiceField(queryset=Author.objects.all(), required=False)

    ...

Open views.py and add category_add() and category_update() view as follows:

# view to add new category

@login_required
def category_add(request):

    # If request is POST, create a bound form(form with data)
    if request.method == "POST":
        f = CategoryForm(request.POST)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the add post form

        # If form is invalid show form with errors again
        if f.is_valid():
            # new_category = f.save()
            # new_category = f.save(commit=False)
            # new_category.author = get_user(request)
            # new_category.save()

            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser
                new_category = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                new_category.author = author
                new_category.save()
            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                new_category = f.save()
            else:
                # if author not a superuser
                new_category = f.save(commit=False)
                new_category.author = Author.objects.get(user__username=request.user.username)
                new_category.save()

            return redirect('category_add')

    # if request is GET the show unbound form to the user
    else:
        f = CategoryForm()

    return render(request, 'cadmin/category_add.html', {'form': f})

# view to update CATEGORY
def category_update(request, pk):
    category = get_object_or_404(Category, pk=pk)

    # If request is POST, create a bound form(form with data)
    if request.method == "POST": # If request is POST, create a bound form
        f = CategoryForm(request.POST, instance=category)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the category form

        # If form is invalid show form with errors again
        if f.is_valid():

            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser
                updated_category = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                updated_category.author = author
                updated_category.save()
            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                updated_category = f.save()
            else:
                # if author not a superuser
                updated_category = f.save(commit=False)
                updated_category.author = Author.objects.get(user__username=request.user.username)
                updated_category.save()

            new_category = f.save()
            return redirect(reverse('category_update', args=[category.id]))

    # if request is GET the show unbound form to the user
    else:
        f = CategoryForm(instance=category)

    return render(request, 'cadmin/category_update.html', {'form': f, 'category': category})

Create two new templates category_add.html and category_update.html with the the following code:

cadmin/templates/cadmin/category_add.html

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Add New Category - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'category_list' %}">All Category</a> &#187; Add Category</p>

        <h3>Add Category</h3>

        {{ form.non_field_errors }}

        <form action="" method="post">
                {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.name.label_tag }}</td>
                    <td>
                        {{ form.name.errors }}
                        {{ form.name }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.slug.label_tag }}</td>
                    <td>
                        {{ form.slug.errors }}
                        {{ form.slug }}
                    </td>
                </tr>

                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}

                <tr>
                    <td></td>
                    <td><input type="submit" value="Add Category"></td>
                </tr>

            </table>
        </form>

    </div>

{% endblock %}

cadmin/templates/cadmin/category_update.html

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Add Category - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'category_list' %}">All Category</a> &#187; Update Category</p>

        <h3>Update Category</h3>

        {{ form.non_field_errors }}

        <form action="" method="post">
                {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.name.label_tag }}</td>
                    <td>
                        {{ form.name.errors }}
                        {{ form.name }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.slug.label_tag }}</td>
                    <td>
                        {{ form.slug.errors }}
                        {{ form.slug }}
                    </td>
                </tr>

                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}

                <tr>
                    <td></td>
                    <td><input type="submit" value="Update Category"></td>
                </tr>

            </table>
        </form>

    </div>

{% endblock %}

At last, add the following url patterns to the end of urlpatterns list as follows:

urlpatterns = [
    ...
    url(r'^category/add/$', views.category_add, name='category_add'),
    url(r'^category/update/(?P<pk>[\d]+)/$', views.category_update, name='category_update'),
    ...
]    

Deleting Categories

Add the category_delete() view just after category_update() as follows:

@login_required
def category_delete(request, pk):
    category = get_object_or_404(Category, pk=pk)
    category.delete()
    next_page = request.GET['next']

    return redirect(next_page)

Next, add the following url pattern at the end of urlpatterns list:

urlpatterns = [
    ...
    url(r'^category/delete/(?P<pk>[\d]+)/$', views.category_delete, name='category_delete'),
    ...
]    

Creating Tag Pages

First open forms.py and redefine author field in TagForm class as follows:

...
class TagForm(forms.ModelForm):
    author = forms.ModelChoiceField(queryset=Author.objects.all(), required=False)

...

Creating Tag List Page

In the cadmin's views.py add tag_list() view just after category_delete() view.

# view to add list all tags

@login_required
def tag_list(request):
    if request.user.is_superuser:
        tags = Tag.objects.order_by("-id").all()
    else:

        tags = Tag.objects.filter(author__user__username=request.user.username).order_by("-id")


    tags = pg_records(request, tags, 5)
    return render(request, 'cadmin/tag_list.html', {'tags': tags})

Create a new template named tag_list.html and add the following code to it:

{% extends "cadmin/base_admin.html" %}

{% block title %}
    All Tags - {{ block.super }}
{% endblock %}

{% block main %}

   <div class="main">

        <p>&#187; <a href="{% url 'tag_list' %}">All Tags</a></p>

        <p class="button"><a href="{% url 'tag_add' %}">Add Tag &raquo;</a></p>

        <p class="count">Total Tags: {{ tags.paginator.count }}</p>

            {% for tag in tags %}
                {% if forloop.first     %}
                    <table class="tbl-class">
                        <tr>
                            <th>Name</th>
                            <th>Slug</th>
                            <th>Author</th>
                            <th>Action</th>
                        </tr>
                {% endif %}

                <tr>
                    <td class="post-title">{{ tag.name }}</td>
                    <td>{{ tag.slug }}</td>
                    <td>{{ tag.author.user }}</td>
                    <td>
                        <a href="{% url 'tag_update' tag.id %}">Edit</a> |
                        <a href="{% url 'tag_delete' tag.id %}?next={{ request.get_full_path }}" onclick=" return confirm('Are you sure ?')">Delete</a>
                    </td>
                </tr>

                {% if forloop.last %}
                    </table>
                {% endif %}

            {% empty %}
                No Tags
            {% endfor %}

   </div>

    {% if tags %}

        <div class="pagination">
            <p>
                {% if tags.has_previous %}
                    <a href="?page={{ tags.previous_page_number }}">&lt; Prev</a> |
                {% endif %}

                {% if tags.has_next %}
                    <a href="?page={{ tags.next_page_number }}">Next &gt;</a>
                {% endif %}

                <span>Page {{ tags.number }} of {{ tags.paginator.num_pages }}</span>
            </p>
        </div>

    {% endif %}

{% endblock %}

Next add the tag_list url pattern at the end of urlpatterns list as follows:

urlpatterns = [
    ...
    url(r'^tag/$', views.tag_list, name='tag_list'),
    ...
]

Tag Add/Update Page

In the cadmin's views.py file add tag_add() and tag_update() views as follows:

# view to add new tag

@login_required
def tag_add(request):

    # If request is POST, create a bound form(form with data)
    if request.method == "POST":
        f = TagForm(request.POST)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the add post form

        # If form is invalid show form with errors again
        if f.is_valid():
            # new_category = f.save()
            # new_category = f.save(commit=False)
            # new_category.author = get_user(request)
            # new_category.save()

            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser
                new_tag = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                new_tag.author = author
                new_tag.save()
            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                new_tag = f.save()
            else:
                # if author not a superuser
                new_tag = f.save(commit=False)
                new_tag.author = Author.objects.get(user__username=request.user.username)
                new_tag.save()

            return redirect('tag_add')

    # if request is GET the show unbound form to the user
    else:
        f = TagForm()

    return render(request, 'cadmin/tag_add.html', {'form': f})

# view to update tag
def tag_update(request, pk):
    tag = get_object_or_404(Tag, pk=pk)

    # If request is POST, create a bound form(form with data)
    if request.method == "POST": # If request is POST, create a bound form
        f = TagForm(request.POST, instance=tag)

        # check whether form is valid or not
        # if the form is valid, save the data to the database
        # and redirect the user back to the tag update form

        # If form is invalid show form with errors again
        if f.is_valid():
            # updated_tag = f.save()

            if request.POST.get('author') == "" and request.user.is_superuser:
                # if author is not supplied and user is superuser
                updated_tag = f.save(commit=False)
                author = Author.objects.get(user__username='staff')
                updated_tag.author = author
                updated_tag.save()
            elif request.POST.get('author') and request.user.is_superuser:
                # if author is supplied and user is superuser
                updated_tag = f.save()
            else:
                # if author not a superuser
                updated_tag = f.save(commit=False)
                updated_tag.author = Author.objects.get(user__username=request.user.username)
                updated_tag.save()

            return redirect(reverse('tag_update', args=[tag.id]))

    # if request is GET the show unbound form to the user
    else:
        f = TagForm(instance=tag)

    return render(request, 'cadmin/tag_update.html', {'form': f, 'tag': tag})

Create two new templates tag_add.html and tag_update.html with the following code:

cadmin/templates/cadmin/tag_add.html

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Add New Tag - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

        <p>&#187; <a href="{% url 'tag_list' %}">All Tags</a> &#187; Add Tag</p>

        <h3>Add Tag</h3>

        {{ form.non_field_errors }}

        <form action="" method="post">
            {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.name.label_tag }}</td>
                    <td>
                        {{ form.name.errors }}
                        {{ form.name }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.slug.label_tag }}</td>
                    <td>
                        {{ form.slug.errors }}
                        {{ form.slug }}
                    </td>
                </tr>

                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}

                <tr>
                    <td></td>
                    <td><input type="submit" value="Add Tag"></td>
                </tr>
            </table>
        </form>

    </div>

{% endblock %}

tag_update.html

{% extends "cadmin/base_admin.html" %}

{% block title %}
    Update Tag - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

            <p>&#187; <a href="{% url 'tag_list' %}">All Tags</a> &#187; Update Tag</p>

            <h3>Update Tag</h3>

            {{ form.non_field_errors }}

            <form action="" method="post">
                {% csrf_token %}
                <table>
                <tr>
                    <td>{{ form.name.label_tag }}</td>
                    <td>
                        {{ form.name.errors }}
                        {{ form.name }}
                    </td>
                </tr>
                <tr>
                    <td>{{ form.slug.label_tag }}</td>
                    <td>
                        {{ form.slug.errors }}
                        {{ form.slug }}
                    </td>
                </tr>

                {% if request.user.is_superuser %}
                    <tr>
                        <td>{{ form.author.label_tag }}</td>
                        <td>
                            {{ form.author.errors }}
                            {{ form.author }}
                        </td>
                    </tr>
                {% endif %}

                <tr>
                    <td></td>
                    <td><input type="submit" value="Update Tag"></td>
                </tr>

                </table>
            </form>
    </div>

{% endblock %}

Next, add the following two url patterns at the end of the urlpatterns list as follows:

urlpatters = [
    ...
    url(r'^tag/add/$', views.tag_add, name='tag_add'),
    url(r'^tag/update/(?P<pk>[\d]+)/$', views.tag_update, name='tag_update'),
    ...
]

Deleting Tags

To delete tag add the following view to the cadmin's views.py file.

...
def tag_delete(request, pk):
    tag = get_object_or_404(Tag, pk=pk)
    tag.delete()
    next_page = request.GET['next']

    return redirect(next_page)
...

Then, add the following url pattern to the urls.py file as follows:

urlpatterns = [
    ...
    url(r'^tag/delete/(?P<pk>[\d]+)/$', views.tag_delete, name='tag_delete'),
    ...
]    

Creating Account Info Page

In this section we will create Account Info page, which will display details of the logged in user. In views.py file add the following view at the end of the file:

@login_required
def account_info(request):
    return render(request, 'cadmin/account_info.html')

Create a template called account_info.html and add the following code to it:

{% extends "cadmin/base_admin.html" %}

{% block title %}
    All Posts - {{ block.super }}
{% endblock %}

{% block main %}

    <div class="main">

    <p>&#187; <a href="{% url 'account_info' %}">Account Info</a></p>

        <table>
            <tr>
                <td>Username</td>
                <td>{{ request.user.username }}</td>
            </tr>
            <tr>
                <td>Email</td>
                <td>
                    {% if request.user.email %}
                    {{ request.user.email }}
                    {% else %}
                        NA
                    {% endif %}
                </td>
            </tr>
            <tr>
                <td>Date Joined</td>
                <td>{{ request.user.date_joined }}</td>
            </tr>
            <tr>
                <td>Last Login</td>
                <td>{{ request.user.last_login }}</td>
            </tr>
        </table>

    </div>

{% endblock %}

At last, open urls.py and add the following url pattern at the end of the urlpatterns list :

urlpatters = [
    ...
    url(r'^account-info/$', views.account_info, name='account_info'),
]

To view logged in user details visit http://127.0.0.1:8000/cadmin/account-info/.

Sidebar Links

We have created all the required pages for our cadmin app. The only thing remain is to add links to pages in the sidebar on the left. Open base_admin.html and modify the links in sidebar as follows as follows:

...
<div class="sidebar">
    <ul>
        <li><a href="{% url 'post_list' %}">Post</a></li>
        <li><a href="{% url 'category_list' %}">Category</a></li>
        <li><a href="{% url 'tag_list' %}">Tag</a></li>
        <li><a href="{% url 'account_info' %}">Account Info</a></li>
        <li><a href="{% url 'password_change' %}">Change Password</a></li>
        <li><a target="_blank" href="{{ request.scheme }}://{{ request.get_host }}">View Site</a></li>
        <li><a href="{% url 'logout' %}">Logout</a></li>
        <li></li>
    </ul>
</div>
...

Nothing new here except request.get_host function. The request object (HttpRequest) has a method called get_host() which returns the hostname. Our cadmin is almost complement. In the next lesson we will learn how to integrate a WYSIWYG editor in Django admin and our custom built cadmin panel.