Assume your blog has been successfully deployed online. You write great articles, interact with your fans and feel a sense of accomplishment.

Now you want to take it one step further and work on improving the quality of your posts, making them more popular and creating the best blog in the industry. The question is how do you judge an article to be “popular”? Page views are one way to do this, but they don’t tell the difference between the clickbait headlines. It’s also a good idea to rely on comment counts, but personal blogs usually don’t have many readers, and it’s normal to have zero comments on good articles.

This is where the “like” function becomes important. If most readers give a “like”, that means the article is really good.

Think before you do it

Praise function is not simple, to achieve the way is very much. Don’t jump in and think: What kind of likes do we really need?

** First, does a “like” require a user to be logged in? ** The advantage of requiring a login is that you can accurately record which users and which articles have been liked (many-to-many relationships) for detailed data analysis. The downside is that the login requirement is cumbersome and blocks most visitors. Bloggers tend not to require users to log in, since small sites usually have few users, and increasing engagement is the core task of liking.

If your site is popular one day, leave the interactive feature requiring users to log in to favorites.

** Second, can users repeat likes? ** Users of many video platforms can like a certain female anchor crazily to show that they like her very, very much. This is fine for platforms with a large number of users, because when you have a large number of users, a few hundred likes is just a drop in the bucket. But it’s easy to get zero thumbs up on some posts and surprisingly high thumbs up on others. Obviously this does not represent a difference in the quality of the article.

Ok, so our current policy is not to require users to log in or to allow them to like things twice. ** The next question is, where do you record data about users’ likes? ** The number of likes is of course stored in a database so that it can be retrieved and presented at any time.

But the question is where is the record of ** verifying whether the user has liked it? ** Recording the user’s IP address in the database is one way to do this, but you have to deal with the relationship between recording the IP address and logging in the user, which is slightly cumbersome. In addition, every user’s “like” needs to send a verification request to the back end, which increases the burden of the server.

Since saving data in the backend database is not good, can ** save data in the browser? ** The answer is yes, and there are cookies and LocalStorage that allow you to save data. The main differences are as follows:

features Cookie LocalStorage
The life cycle You can set the expiration time. By default, the expiration time is after the browser is closed It is stored permanently unless it is removed
The storage space About 4 k As a general rule, be 5 MB
Communicating with the server This is carried each time in the HTTP header Does not participate in server communication
Ease of use The source-generated interface is unfriendly Source interfaces are acceptable

By comparison, it can be found that LocalStorage can permanently save data, large storage space, and does not participate in server communication, which is very suitable for the demand of “like”. Since the data is stored in the browser, there is no need to distinguish whether the user is logged in or not: each “like” request actually checks whether the current browser has already been liked, not the user!

You might argue that users could have liked it again and again if they had moved to a different browser, especially since browser data is so easily tampered with. But what does it matter? Like data doesn’t have to be exact, so let it go.

All modern browsers support LocalStorage. If you’re still using IE6, consider upgrading your browser.

To sum up, our likes are as follows:

  • No user login is required
  • Double likes are not allowed
  • The number of likes is stored in the server database
  • The upvote verification data is saved in the LocalStorage of the browser

When the user likes the file, the front-end script checks whether the file has been liked in LocalStorage. If not, the server will send a “like” request and record the data.

Think about the need and the problem will be solved. The next step is code implementation.

It should be noted that the above analysis does not mean that other methods are bad, but only the technical path that bloggers feel is appropriate in the context of small blog sites. If you have another Hamlet in you, find a way to make it happen.

Code implementation

The preparatory work

The focus of this chapter is on the front end, so write the simple back-end code first, just as a warm-up.

Some readers get headaches when they hear the front end. I understand your pain, but it’s necessary. You can’t make a beautiful website just by writing Python.

Since the likes need to be saved in the database, it is necessary to modify the article model:

article/models.py

...
# Article model
class ArticlePost(models.Model):.# Added "like" statistics
    likes = models.PositiveIntegerField(default=0)...Copy the code

Migrating data:

(env) > python manage.py makemigrations
(env) > python manage.py migrate
Copy the code

Continue with the class view:

article/views.py

...
# Likes +1
class IncreaseLikesView(View):
    def post(self, request, *args, **kwargs):
        article = ArticlePost.objects.get(id=kwargs.get('id'))
        article.likes += 1
        article.save()
        return HttpResponse('success')
Copy the code

The function increases the number of likes by 1 and returns SUCCESS. As for why it is success, I will talk about it later.

Finally, there is the routing:

article/urls.py

...
urlpatterns = [
    ...
    Thumb up + # 1
    path(
        'increase-likes/<int:id>/', 
        views.IncreaseLikesView.as_view(), 
        name='increase_likes'),]Copy the code

That’s easy. All that’s left is to focus on writing front-end code.

JS with Ajax

Because the validation data is stored in the browser, there is a lot of front-end work.

Post the full code first (explained later) :

templates/article/detail.html

...

<! -- Existing code, text -->
<div class="col-12">
    <p>{{ article.body|safe }}</p>
</div>

<! -- Added "like" button -->
<div style="text-align:center;" class="mt-4">
    <button class="btn btn-outline-danger"
            type="button"
            onclick="validate_is_like( '{% url 'article:increase_likes' article.id %}', {{ article.id }}, {{ article.likes }} )"
            >
        <span>give a like</span>
        <span>
            <i class="fas fa-heart"></i>
        </span>
        <span id="likes_number">
            {{ article.likes }}
        </span>
    </button>
</div>. {% block script %} ...<! -- new code -->

<! -- csrf token -->
<script src="{% static 'csrf.js' %}"></script>
<script>
    // Like the main function
    function validate_is_like(url, id, likes) {
        // Fetch data from LocalStorage
        let storage = window.localStorage;
        const storage_str_data = storage.getItem("my_blog_data");
        let storage_json_data = JSON.parse(storage_str_data);
        // Create an empty dictionary if the data does not exist
        if(! storage_json_data) { storage_json_data = {} };// Check if the current post has been liked. If yes, status = true
        const status = check_status(storage_json_data, id);
        if (status) {
            layer.msg('It's already been liked.');
            // Exit the function immediately if you like it
            return;
        } else {
            // Find the number of likes with Jquery and add +1
            $('span#likes_number').text(likes + 1).css('color'.'#dc3545');
        }
        // Use Ajax to send post requests to the back end
        $.post(
            url,
            // Post is only for CSRF validation, so data is null
            {},
            function(result) {
                if (result === 'success') {
                    // Try to modify the like data
                    try {
                        storage_json_data[id] = true;
                    } catch (e) {
                        window.localStorage.clear();
                    };
                    // Convert the dictionary to a string for storage in LocalStorage
                    const d = JSON.stringify(storage_json_data);
                    // Try to store thumbs-up data to LocalStorage
                    try {
                        storage.setItem("my_blog_data", d);
                    } catch (e) {
                        // code 22 error indicates that LocalStorage space is full
                        if (e.code === 22) {
                            window.localStorage.clear();
                            storage.setItem("my_blog_data", d); }}; }else {
                    layer.msg("Failed to communicate with server.. Try again later."); }}); };// Support the main function to verify the status of the thumbs-up
    function check_status(data, id) {
        // Try to query the "like" status
        try {
            if (id in data && data[id]) {
                return true;
            } else {
                return false; }}catch (e) {
            window.localStorage.clear();
            return false;
        };
    };
</script>
{% endblock script %}
Copy the code

It’s a lot of code, so let’s break it down.

<! -- Add "like" code -->
<div style="text-align:center;" class="mt-4">
    <button class="btn btn-outline-danger"
            type="button"
            onclick="validate_is_like( '{% url 'article:increase_likes' article.id %}', {{ article.id }}, {{ article.likes }} )"
            >
        <span>give a like</span>
        <span>
            <i class="fas fa-heart"></i>
        </span>
        <span id="likes_number">
            {{ article.likes }}
        </span>
    </button>
</div>
Copy the code

The HTML code above is simple, providing a “like” button that triggers a JavaScript function called validate_is_like when clicked. Note in particular that ‘{% URL ‘article:increase_likes’ article.id %}’ are always in single quotation marks (no double quotation marks are allowed).

<script src="{% static 'csrf.js' %}"></script>
Copy the code

Remember cSRf.js? We introduced it in the multi-level comments section to make Ajax CSRF compliant. If you don’t have this file, please click the link to download it.

Next up is the most popular function, validate_is_like(), which splits the contents.

// Fetch data from LocalStorage
let storage = window.localStorage;
const storage_str_data = storage.getItem("my_blog_data");
let storage_json_data = JSON.parse(storage_str_data);
// Create an empty dictionary if the data does not exist
if(! storage_json_data) { storage_json_data = {} };Copy the code

In the browser, the window object refers to the current browser window. It is also the top level object of the current page, and all other objects are subordinate to it, as is localStorage.

To validate data, data must first be retrieved. Here the data is retrieved using the localstorage.getitem () interface.

Although LocalStorage is stored as a standard key-value pair type (similar to Python dictionaries), it is odd that the stored values only support strings. Parse () is used to restore the string to an object.

There is no data in LocalStorage the first time a user likes it, so the if statement creates an empty dictionary to be used.

// Check if the current post has been liked. If yes, status = true
const status = check_status(storage_json_data, id);
if (status) {
    layer.msg('It's already been liked.');
    // Exit the function immediately if you like it
    return;
} else {
    // Find the number of likes with Jquery and add +1
    $('span#likes_number').text(likes + 1).css('color'.'#dc3545');
}
Copy the code

The function check_status is immediately called to check if the user has already liked the article. If the validATE_is_like function is clicked, it pops up and terminates the validate_is_like function with return. If it hasn’t already been clicked, increase the likes of the button by 1.

But at this time in fact, the backstage database has not updated the number of likes. And then we look down.

// Use Ajax to send post requests to the back end
$.post(
    url,
    // Post is only for CSRF validation, so data is null
    {},
    function(result) {
        if (result === 'success') {
            // Try to modify the like data
            try {
                storage_json_data[id] = true;
            } catch (e) {
                window.localStorage.clear();
            };

            const d = JSON.stringify(storage_json_data);
            // Try to store thumbs-up data to LocalStorage
            try {
                storage.setItem("my_blog_data", d);
            } catch (e) {
                // code 22 error indicates that LocalStorage space is full
                if (e.code === 22) {
                    window.localStorage.clear();
                    storage.setItem("my_blog_data", d); }}; }else {
            layer.msg("Failed to communicate with server.. Try again later."); }});Copy the code

Here we start trying to communicate with the back end and update the likes count. The entire block of code is wrapped in $.post(), which is essentially an Ajax POST request. function(result) {… } is the callback function that is executed when the request succeeds, and result is the return value on the back end. If the communication is successful, try to save the upvoted verification data to LocalStorage. Any errors that occur (especially when LocalStorage is full) will clear all data in LocalStorage for subsequent data logging.

As you can see, the data structure used by bloggers is relatively simple, like this:

{
    2: true,
    31: true
    ...
}
Copy the code

The key represents the id of the article, and the Boolean value represents the status of the like. The above data indicates that the posts with ids 2 and 31 have been liked. Readers may later expect posts, comments, and other content to be liked, requiring more complex data structures.

// Support the main function to verify the status of the thumbs-up
function check_status(data, id) {
    // Try to query the "like" status
    try {
        if (id in data && data[id]) {
            return true;
        } else {
            return false; }}catch (e) {
        window.localStorage.clear();
        return false;
    };
};
Copy the code

The check_status() function is simple. It checks to see if the “like” has been given, and returns true if so, and false otherwise.

The entire JavaScript script is complete.

Debugging interface

Please press Ctrl + Shift + I to open the Console interface of the browser Console and run the following command debug:

  • LocalStorage: View the data of localStorage
  • Localstorage.clear () : clears all data
  • Localstorage.getitem () : gets some data
  • Localstorage.setitem () : Saves some data

test

Now that the code is finished, let’s open the article details page to test it:

Click the “like” button, the “like” number +1; Clicking the “like” button again will not increase the number of “likes” and will prompt the user to click the “like” button.

Feel free to close the page or browser, and the saved “like” data won’t disappear.

This completes a simple “like” function.

Of course, you can continue to optimize:

  • Unliked hearts should be shown in gray, clicked hearts in red, so that it is humanized
  • Better yet, add some cool thumbs-up animations or prompt text
  • Should I send a notification to the person I like?
  • .

This tutorial is limited in space, so I’m not going to go any further. It should be done by heart as a homework for readers. Give you a thumbs up!

The first tip: when the initial page is loaded, the love is uniformly displayed in gray. Then call JavaScript script to compare the data in LocalStorage and flexibly use Jquery to change the color of the love that has been liked to red.

conclusion

Our blog project now has a hierarchical user interaction structure: minimal traffic data, rating the popularity of post types; The “like” data is balanced to evaluate the popularity of the content of the article; Review data is the most cumbersome, but also the most valuable. Readers need to think through the core requirements in the same way as they develop features in the future.

Another point to make is that only non-sensitive, non-important data is stored in LocalStorage, so don’t rely too much on it.

Again, the code files are getting bigger and bigger for tutorial purposes. Break it up into smaller components where appropriate for easy maintenance and reuse.


  • If you have any questions please leave a message on Doucet’s personal website and I will reply as soon as possible.
  • Or Email me a private message: [email protected]
  • Project code: Django_blog_tutorial