Modifying Post Add Page

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

Further, It will not be mandatory for the superuser to select the author while adding a post. Because we will automatically assign a special account named **staff** to the post in case the author is not selected. Okay, let’s implement this:

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 a non-superuser account and try creating a new post.

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 the if tag (line 58) in post_add.html as follows:

TGDB/django_project/cadmin/templates/cadmin/post_add.html

1

2

3

4

5

6

7

8

9

10

11

12

{#...#}

{#Showauthorsonlyifloggedinuserisasuperuser#}

{#{%ifrequest.user.is_superuser%}#}

<tr>

<td>{{ form.author.label_tag }}</td>

<td>

{{ form.author.errors }}

{{ form.author }}

</td>

</tr>

{#{%endif%}#}

{#...#}

Hit the save button again and you will be displayed a form like this:

One way to solve this problem is to make author field optional in PostForm class. To do so, modify PostForm class in blog’s forms.py as follows:

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

Next, 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 with username staff by visiting http://127.0.0.1:8000/cadmin/register/ page, then verify the account the by clicking email verification link sent to the email. Hop back to the Django shell and set the is_superuser attribute of the user we just created to True.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

>>>

>>>fromblog.models importAuthor

>>>

>>>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.user.save()

>>>

Instead of using Django Shell, we could have also used Django Admin for this change.

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

TGDB/django_project/cadmin/views.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

#...

@login_required

defpost_add(request):

# If request is POST, create a bound form(form with data)

ifrequest.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

iff.is_valid():

# if author is not selected and user is superuser, then assign the post to the author named staff

There is nothing new in the above code, except commit=False and save_m2m() method. As you already know, calling save() method on a ModelForm class saves the object into 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 models, 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 labor login to cadmin app using a superuser account (like the staff) 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 and hit “Add Post” to create a new post.

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

TGDB/django_project/cadmin/views.py

1

2

3

4

5

6

7

8

9

#...

# if author is not selected and user is superuser, then assign the post to the author named staff

ifrequest.POST.get('author')==""andrequest.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:

TGDB/django_project/cadmin/views.py

1

2

3

4

5

#...

# if author is selected and user is superuser

elifrequest.POST.get('author')andrequest.user.is_superuser:

new_post=f.save()

#...

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

As you can see the logged in user is not allowed to select author. Enter some data in the fields and hit “Add Post” button. This action will execute else block in the post_add() view:

The post_list() view displays a list of post in reverse chronological order (i.e latest first, oldest last). If logged in user is a superuser then it will display all the posts in the database. On the other hand, if logged in user is non-superuser it will only display posts created by himself.

Sidebar Links

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

Nothing new here except request.get_host function. The request object (HttpRequest) has a method called get_host() which returns the hostname. At this point, cadmin app is fully functional, use it to create some new post, categories, and tags.

In the next lesson, we will learn how to integrate a WYSIWYG editor in Django Admin and our custom built cadmin app.

Note: To checkout this version of the repository type git checkout 36a.