Wyświetlanie w modalu, dynamicznych treści z użyciem Django REST framework

Jakiś czas temu, narodził się u mnie pomysł na technologicznego bloga. Ze względu na chęć użycia w nim dynamicznego ładowania treści, które następnie były by prezentowane w modalu, przystąpiłem do pracy :)


1. Backend

W poniższym przykładzie, wykorzystamy bibliotekę Django REST framework, którą instalujemy po przez:

pip install djangorestframework django-admin-sortable2

Swoją przygodę rozpocząłem od stworzenia modelu postu oraz kategorii zrzeszającej posty. 

plik: blog/models.py

from datetime import datetime
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _


class Category(models.Model):
name = models.CharField(_('Name'), max_length=120)
iconTag = models.CharField(_('Html icon tag'), max_length=120)

class Meta:
verbose_name = _('Category')
verbose_name_plural = _('Category')

def __str__(self):
return self.name


class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('Autor'), null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name=_('Category'))
title = models.CharField(_('Title'), max_length=120)
description = models.TextField(_('Description'), )
createdDate = models.DateTimeField(_('Created date'), default=datetime.now, blank=True)

visible_choice = [
('P', 'Public'),
('N', 'Hidden'),
]

visible = models.CharField(
_('Visible'),
max_length=1,
choices=visible_choice,
default="P",
)

class Meta:
verbose_name = _('Post')
verbose_name_plural = _('Posts')

def __str__(self):
return self.title

Następnie dodałem serializer dla konkretnego Postu, celem umożliwienia przesyłania go dalej w sposób ustruktyzowany.

plik: blog/serializers.py

from rest_framework import serializers

from .models import Post, Category

class PostSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)

class Meta:
model = Post
fields = ['id', 'category_name', 'title', 'description', 'createdDate']

*Samo dodanie 6 linijki, wiązało się z wykorzystaniem klucza ForeignKey,który dzięki temu zwraca nam tytuł kategorii, zamiast tylko jej id.


Idąc dalej utworzyłem standardowy plik z adresami, który zwróci nam stronę główną, jak i szczegóły danego postu przez api.

plik: blog/urls.py

from django.urls import path

from . import views, apiviews

urlpatterns = [
path('', views.PostListView.as_view(), name='blog_all'),
path('api/posts/list/<id>', apiviews.PostDetailAPI.as_view(), name='api_posts_detail')
]

Tutaj standardowo, skorzystałem z generycznego widoku ListView, który zwróci nam listę postów na stronie głównej. Z racji że to blog, zastrzegamy w kodzie że chcemy wyświetlić posty które zostały opublikowane.

plik: blog/views.py

from django.views.generic.list import ListView

from .models import Post


class PostListView(ListView):
model = Post
template_name = 'blog/index.html'
paginate_by = 10
ordering = ['-my_order']
queryset = Post.objects.filter(visible='P')

Można tu też zauważyć sortowanie po my_order, kolumnie w której skorzystałem z możliwości tworzenia własnego porządku dla danych obiektów (więcej pod adresem Dokumentacja django-admin-sortable2)

Dodatkowo stworzyłem api, które zwróci nam listę postów jak i szczegóły danego modelu, bazując na otrzymanym w adresie strony parametrze id.

plik: blog/apiviews.py

from rest_framework import generics

from .models import Post
from .serializers import PostSerializer


class PostListAPI(generics.ListAPIView):
model = Post
serializer_class = PostSerializer
queryset = Post.objects.all()


class PostDetailAPI(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = Post.objects.all()
serializer_class = PostSerializer

2. Frontend

Utworzyłem szablon renderujący z pliku views.py, listę postów oraz poniżej szkielet modalu, na którym następnie wyświetlimy posty.

plik: blog/templates/blog/index.html

{% extends 'base.html' %}

{% block content %}
<section id="hero" class="d-flex align-items-center">
<div class="container">
<div class="row icon-boxes" id="blog">
{% for article in object_list %}
<div class="col-md-6 col-lg-3 d-flex align-items-stretch mb-4" data-aos="zoom-in"
data-aos-delay="200">
<a href="#post-{{ article.id }}" class="open-post-dialog icon-box" data-toggle="modal"
data-id="{{ article.id }}">
<div class="icon">{{ article.category.iconTag | safe }}</div>
<h4 class="title">{{ article.category.name }}</h4>
<p class="description">{{ article.title }}</p>
</a>
</div>
{% empty %}
<p class="mb-2">No posts</p>
{% endfor %}
</div>
</div>
</section>
{% block endblock %}

{% block modal %}
<div class="modal fade" id="showPostDialog" tabindex="-1" aria-labelledby="postModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="category"></h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="postModalBody">
<blockquote class="blockquote">
<h4 id="title" class="mb-3"></h4>
<footer class="blockquote-footer">autor: Kamil Mirończuk</footer>
</blockquote>
<div id="content"></div>
<div class="row">
<div class="col-6">
<a href="" id="post-url" class="btn btn-primary w-100">Przejdź</a>
</div>
<div class="col-6">
<button type="button" class="btn btn-secondary w-100" data-bs-dismiss="modal">Zamknij</button>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

Całą obsługę formularza, wykonałem z poziomu javascriptu, wykorzystując do tego funkcję fetch.

plik: blog/static/assets/js/modal.js

$(document).on("click", ".open-post-dialog", function () {
let postId = $(this).data('id');
let url = "/api/posts/list/" + postId + "?format=json";

fetch(url)
.then(res => res.json())
.then(data => {
$(".modal-content #category").html(data.category_name);
$(".modal-content #title").html(data.title);
$(".modal-content #content").html(data.description);

$( "#hero" ).addClass( "is-blurred" );
$( "#header" ).addClass( "is-blurred" );
$('#showPostDialog').modal('show');
}
).catch(err => {
throw err
}
);

$('#showPostDialog').on('hidden.bs.modal', function () {
$( "#header" ).removeClass( "is-blurred" );
$( "#hero" ).removeClass( "is-blurred" );
})
});

*Należy również pamiętać o dodaniu biblioteki jquery do strony.

W celu polepszenia odczuć podczas czytania artykułu, podczas wyświetlania modalu, zastosowałem technikę blurowania tła po przez dodanie wpisu css.

plik: static/assets/css/style.css

.is-blurred {
filter: blur(5px);
-webkit-filter: blur(5px);
}

Ogarnięcie całego projektu nie powinno przysporzyć Ci trudności. Celowo nie rozdrabniałem się nad każdą linijką, celem zmotywowania do stworzenia własnego oryginalnego bloga na podstawie powyższego kodu:)

Powodzenia!

Kamil Mirończuk

I kiedy czegoś gorąco pragniesz, to cały wszechświat sprzyja potajemnie twojemu pragnieniu
~Paulo Coelho

Komentarze

Zostaw komentarz

Twój adres mailowy NIE zostanie opublikowany. W razie otrzymania zapytania, otrzymasz na niego odpowiedź.
Wymagane pola są oznaczone jako *