Advanced Django Blog with Rest API and GraphQL
Main Project Files on Github
In this article I will review a project I created using the following frameworks:
- Project Description.
- User Story.
- Get started with the project
- Main Project Review.
- GraphQL Review.
- REST API Review.
Project Description:
This project is called blog_backend
and it has four registered apps and one third-party app.
The
blog
app which contains an app-level templates and urls, used for most of the functionalities of our project, like, blog models, forms, views, urls, and custom template tags.The
api
app which contains the Django Rest Framework integration used to build a REST API.The
graphql_app
which contains the Graphene Django integration used to build a GraphQL endpoint.The
users
app which usesdjango.contrib.auth.urls
to allow users to register and login to their accounts.crispy forms
third-party app which beautify django forms UI design using Bootstrap.
User Story
Users can register for a new account and login, once logged in users can confirm their email address (currently using Mail hog which is handled by Cookiecutter Django).
Once authenticated, users can do the following:
Update their name or email address inside their profile page.
Create, Read, Update and Delete (CRUD) blog posts on the website.
Search for the blog posts.
Mark posts as their favorite posts and access them in their profile dropdown menu .
Access their profile which lists all their added blog posts and their favorite posts.
Add comments on blog posts, but the comments will not be visible until the website admin approves it.
Access a GraphQL endpoint and run several Queries and CRUD Mutations.
Access a Rest API endpoint and run CRUD operations.
To get started with this project
Make sure that Docker and docker-compose are installed in your system.
Clone the repository: git clone https://github.com/MoustafaShaaban/Advanced_Django_Blog.git
Change directory to blog_backend directory
cd blog_backend
Build the docker image to develop the project locally using docker-compose:
docker-compose -f local.yml build
- Create the database by running the following commands:
docker-compose -f local.yml run --rm django python manage.py makemigrations
docker-compose -f local.yml run --rm django python manage.py migrate
- Create a super user:
docker-compose -f local.yml run --rm django python manage.py createsuperuser
- Now run the project:
docker-compose -f local.yml up
- Open the web browser and go to
http://localhost:8000/
to see the results.
Now, we can start reviewing our project.
Main Project Review
- Adding a new blog post:
- Updating a blog post:
- Delete a blog post:
- Protect blog posts so that only the author of the post who can modify it:
- Add blog posts to favorite posts:
- List all tags and its related posts:
- Search for blog posts:
GraphQL Review
Currently, the project has the following GraphQL Queries and Mutations:
allTags
Lists all the available tags in the website:
query ReturnAllTags {
allTags {
id
name
slug
}
}
createTag
This mutation will do the following:
Create a tag called Python, it takes only one input >> the tag name, then, Django will use slugify to create a slug for the tag before saving it to the database.
Return all the information about this tag (id, name, and slug):
mutation CreateTag {
createTag(input: {name: "Python"}) {
tag {
id
name
slug
}
}
}
updateTag
This mutation will do the following:
Update the tag we need by its slug. so we first need to get the slug of the tag we need to update, then pass in the new name we want to.
Return all the information about this tag (id, name, and slug):
mutation UpdateTag {
updateTag(slug: "python", name: "python") {
tag {
id
name
slug
}
}
}
deleteTag
This mutation will do the following:
Select the tag by its id then, deletes it.
Return a success Boolean, if its True, then the tag is deleted successfully:
mutation DeleteTag {
deleteTag(id: 2) {
success
}
}
createPost
This mutation will do the following:
Create a new blog post, it takes the following inputs => title, content and tags.
Return an instance of the post and some of its information like post id, title, slug, author, tag, content and comments:
mutation createPost {
createPost(input: {
title: "Post 1",
content: "Post 1 content",
tags: [
{ slug: "python" },
]
}) {
post {
id
title
slug
author {
username
}
tag {
name
slug
}
content
comments {
comment
user {
username
}
}
}
}
}
updatePost
This mutation is protected, meaning that, only the post author who can update it, If another user tried to update the post, a GraphQL Error will be raised. and it does the following:
Update a blog post, it selects the post by its id, then takes the following inputs => title, content and tags.
Return an instance of the post and some of its information like post id, title, slug, author, tag, content and comments:
mutation updatePost {
updatePost(
id: 1
input: {title: "Post 1 updated", content: "Post 1 content updated", tags: [{slug: "python"}, {slug: "django"}]}
) {
post {
title
tag {
name
slug
}
content
}
}
}
deletePost
This mutation is protected, meaning that, only the post author who can delete it, If another user tried to delete the post, a GraphQL Error will be raised. and it does the following:
Select the post by its id, then deletes it.
Return a success Boolean, if its True, then the post is deleted successfully:
mutation deletePost {
deletePost(id: 1) {
success
}
}
createComment
This mutation will do the following:
Adds a new comment to a blog post, it takes the following inputs => postSlug, email and comment.
Return an instance of comment and some of its information like comment id, user, email, comment and the post title which has this comment:
mutation createComment {
createComment(
inputs: {postSlug: "post-1", email: "admin@admin.com", comment: "Great post"}
) {
comment {
id
user {
username
}
email
comment
post {
title
}
}
}
}
updateComment
This mutation is protected, meaning that, only the user who added this comment can update it, If another user tried to update the comment, a GraphQL Error will be raised. and it does the following:
Update a comment, it selects the comment by its id, then takes the following inputs => comment.
Return an instance of the comment and some of its information like comment id and comment:
mutation updateComment {
updateComment(id: 1, comment: "Great post keep it up") {
success
comment {
id
comment
}
}
}
deleteComment
This mutation is protected, meaning that, only the user who added this comment can delete it, If another user tried to update the comment, a GraphQL Error will be raised. and it does the following:
Select the comment by its id, then deletes it.
Return a success Boolean, if its True, then the comment is deleted successfully:
mutation deleteComment {
deleteComment(id: 2) {
success
}
}
allPosts
This Query returns all the blog posts:
query AllPosts {
allPosts {
id
title
author {
username
}
tag {
name
}
content
comments {
comment
user {
username
}
}
}
}
allPostsWithFilters
This is a GraphQL Relay Mutation which uses Django-Filter package to filter the posts. Here, we return the posts which its title contains the number 1:
query AllPostsWithFilters {
allPostsWithFilters(title_Icontains: "1") {
edges {
node {
id
title
author {
username
}
}
}
}
}
We can also filter the blog posts by content, and display specific number of posts. (Check the GraphQL Docs at the following project url: http://127.0.0.1:8000/graphql/ )
postsByTag
This Query return all the posts which has the provided tag.
query PostsByTag {
postsByTag(tag: "python") {
id
title
author {
username
}
tag {
name
}
content
comments {
comment
user {
username
}
}
}
}
postsByTAuthor
This Query return all the posts which has the provided author username.
query PostsByAuthor {
postsByAuthor(author: "admin") {
id
title
author {
username
}
tag {
name
}
content
comments {
comment
user {
username
}
}
}
}
postByTitle
This Query returns all the posts filtered by its title (This is not a GraphQL Relay Query like the one above)
query PostsByTitle {
postByTitle(title: "Post") {
id
title
author {
username
}
tag {
name
}
content
comments {
user {
username
}
comment
}
}
}
commentsByPost
This Query return all the comments on a specific blog post.
query CommentsByPost {
commentsByPost(postTitle: "Post") {
id
comment
email
user {
username
}
post {
title
author {
username
}
}
}
}
REST API Review
The REST API has the same functionality as the main website, It uses Django REST Framework Viewsets and Routers to allow users to perform Create, Read, Update and Delete (CURD) operations.
Also It uses Cookiecutter Django's REST API Documentation using Swagger API.
I changed the default way to retrieve the blog post detail information, Instead of having to provide the post id, you can uses the post slug: