Frontend untuk si Backend - Django Unicorn PART 2

Dalam tulisan sebelumnya sudah mencoba fungsi-fungsi dasar unicorn, dalam kali ini akan buat untuk CRUD. Pertama mari buat bagian list.

list-contact.py

Berikut isi komponen list-contact.py

from django.shortcuts import redirect
from django_unicorn.components import LocationUpdate, QuerySetType, UnicornView

from contact.models import ContactForm


class ListContactView(UnicornView):
    data: QuerySetType[ContactForm] = ContactForm.objects.none()
    search: str = ""

    def mount(self):
        """On mount, populate the movies property w/ a QuerySet of all movies"""
        self.load_data()

    def load_data(self, query=None):
        datas = ContactForm.objects.all()
        if query:
            datas = datas.filter(name__icontains=query)
        datas = datas.order_by("-id")
        self.datas = datas

    def updated_search(self, query):
        self.load_data(query)

    def delete(self, id):
        ContactForm.objects.filter(id=id).delete()
        self.mount()
        self.reset()

Mari bahas perblok

data: QuerySetType[ContactForm] = ContactForm.objects.none()
search: str = ""

Properti atau variable untuk menampung data:

  • data: Menampung untuk querysety dari contactform
  • search: Menampung keyword search yang akan ditrigger dari komponen list-contact.html
def mount(self):
        """On mount, populate the movies property w/ a QuerySet of all movies"""
        self.load_data()

def load_data(self, query=None):
    data = ContactForm.objects.all()
    if query:
    	data = data.filter(name__icontains=query)
    data = datas.order_by("-id")
    self.data = data

def updated_search(self, query):
     self.load_data(query)

Fungsi mount() adalah fungsi bawaan dari unicorn yang akan dijalankan ketika komponen ini di-load

Fungsi load_data() adalah fungsi yang dibuat untuk mencari data dari model terkait, ini bukan fungsi bawaan, ini yang dibuat sendiri

Fungsi updated_search() adalah fungsi bawaan yang akan mendeteksi perubahan pada variable search, ini formatnya updated_<nama_variable>()

def delete(self, id):
        ContactForm.objects.filter(id=id).delete()
        self.mount()
        self.reset()

Sesuai namanya ini fungsi untuk menghapus data dari contactForm, sedangkan untuk self.mount() untuk memastikan data ke-refresh, sedangkan reset untuk mengambalikan ke state awal.

Untuk tampilannya bbah komponen list-contact.html menjadi seperti berikut:

<div>
  <div class="float-right mb-4">
    <a href="{% url 'add-form' %}" class="btn btn-success"> Add Form </a>
 </div>
    <!--Section: Contact component list-->
    <div class="row ">
      <div class="col-4">
      <input unicorn:model="search" type="text" id="myInput" class="form-control" placeholder="Search for names..">
      </div>
    </div>
    <table class="table table-hover">
        <thead>
          <tr>
            <th scope="col">Name</th>
            <th scope="col">Email</th>
            <th scope="col">Action</th>
          </tr>
        </thead>
        <tbody>

        {% for single in data %}
        <tr>
            <td>{{ single.name }}</td>
            <td>{{ single.email }}</td>
            <td>
                <a class="btn btn-success"  href="{% url 'edit-form' id=single.id %}">Update</a>
                <button class="btn btn-danger"
                 onclick="swal.fire({ 
                    icon: 'warning',
                    title: 'Are you sure??',
                    text: 'data deleted permanently!!',
                    showCancelButton: true,
                    confirmButtonText: 'Confirm',
                    cancelButtonText: 'Cancel',
                    }).then((result) => {
                    if (result.isConfirmed) {
                        Unicorn.call('list-contact', 'delete', '{{ single.id}}')
                    } 
                    })">
                Delete</button>
            </td>
        </tr>
        {% endfor %}
          
        </tbody>
      </table>
</div>

Mari bahas "perbaris"

Pertama

 <div class="float-right mb-4">
    <a href="{% url 'add-form' %}" class="btn btn-success"> Add Form </a>
 </div>

Ini masih django template sederhana, itu sebuah link untuk menuju path add-form alias untuk menambahkan kontak form

Pastikan url atau views tujuan sudah disiapkan, sengaja gak bahas itu karena itu tidak ada sangkut pautnya langsung dengan unicorn

<div class="col-4">
      <input unicorn:model="search" type="text" id="myInput" class="form-control" placeholder="Search for names..">
      </div>

kode di atas mirip dengan di tulisan sebelumnya, melakukan request langsung ke komponen list-contact.py

{% for single in data %}
        <tr>
            <td>{{ single.name }}</td>
            <td>{{ single.email }}</td>
            <td>
                <a class="btn btn-success"  href="{% url 'edit-form' id=single.id %}">Update</a>
                <button class="btn btn-danger"
                 onclick="swal.fire({ 
                    icon: 'warning',
                    title: 'Are you sure??',
                    text: 'data deleted permanently!!',
                    showCancelButton: true,
                    confirmButtonText: 'Confirm',
                    cancelButtonText: 'Cancel',
                    }).then((result) => {
                    if (result.isConfirmed) {
                        Unicorn.call('list-contact', 'delete', '{{ single.id}}')
                    } 
                    })">
                Delete</button>
            </td>
        </tr>
        {% endfor %}

Itu looping biasa, bedanya data datang dari list-contact.py dan pada bagian onclick saya memanggil swal untuk modal confirm sedangkan Unicorn.call('list-contact', 'delete', '{{ single.id}}') Ini fungsi dari unicorn yaitu bisa memanggil langsung method di list-contact.py dari javascript.

Sedangkan untuk bagian href="{% url 'edit-form' id=single.id %} ini fungsi url django standar, mengirimkan ke url path edit form dengan param id dari data yang dipilih.

Sehingga hasilnya seperti berikut

Tabel list

Form

Sama dengan sebelumnya perlu buat dulu komponen baru, saya beri nama form-contact sehingga menghasilkan form-contact.py dan form-contact.html

form-contact.py

from django import forms
from django.shortcuts import redirect
from django_unicorn.components import QuerySetType, UnicornView

from contact.models import ContactForm


class FormContact(forms.Form):
    contact_id = forms.IntegerField(required=False)
    name = forms.CharField(max_length=100, required=True)
    email = forms.EmailField(max_length=100, required=True)
    text = forms.Textarea()

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get("email")
        pk = cleaned_data.get("contact_id")

        if email:
            exist = ContactForm.objects.filter(email=email).first()
            if exist:
                if exist.id != pk:
                    self.add_error("email", "email already used")


class FormContactView(UnicornView):
    form_class = FormContact
    name = ""
    email = ""
    text = ""
    contact_id = None


    def mount(self):
        """On mount, populate the movies property w/ a QuerySet of all movies"""
        if self.contact_id:
            self.populate(self.contact_id)

    def populate(self, id):
        detail = ContactForm.objects.get(id=id)
        self.name = detail.name
        self.email = detail.email
        self.text = detail.text
        self.contact_id = id

    def submit(self):
        collect = {"name": self.name, "email": self.email, "text": self.text}

        if self.is_valid():
            if self.contact_id:
                contact = ContactForm.objects.get(id=self.contact_id)
                contact.name = self.name
                if contact.email != self.email:
                    contact.email = self.email
                contact.text = self.text
            else:
                contact = ContactForm(**collect)
            contact.save()
            return redirect("list-contact")

Kembali bahas perblok

class FormContact(forms.Form):
    contact_id = forms.IntegerField(required=False)
    name = forms.CharField(max_length=100, required=True)
    email = forms.EmailField(max_length=100, required=True)
    text = forms.Textarea()

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get("email")
        pk = cleaned_data.get("contact_id")

        if email:
            exist = ContactForm.objects.filter(email=email).first()
            if exist:
                if exist.id != pk:
                    self.add_error("email", "email already used")

Tidak ada yang aneh di bagian ini, ini hanyalah django forms pada umumnya, tidak spesifik django unicorn

form_class = FormContact
name = ""
email = ""
text = ""
contact_id = None
  • form_class = Variable "bawaan" untuk identifikasi bahwa django unicorn akan mengunkana django form yang mana
  • name = Variable yang akan menampung "name"
  • email = Variable yang akan menampung "email"
  • text = Variable ayng akan menampung "text"
  • contact_id = Variable yang akan menampung id
def mount(self):
        """On mount, populate the movies property w/ a QuerySet of all movies"""
        if self.contact_id:
            self.populate(self.contact_id)

def populate(self, id):
    detail = ContactForm.objects.get(id=id)
    self.name = detail.name
    self.email = detail.email
    self.text = detail.text
    self.contact_id = id

Sama seperti sebelumnya, mount(), sedangkan populate() untuk populas data jika id diberikan saat initialisasi komponent

def submit(self):
        collect = {"name": self.name, "email": self.email, "text": self.text}

        if self.is_valid():
            if self.contact_id:
                contact = ContactForm.objects.get(id=self.contact_id)
                contact.name = self.name
                if contact.email != self.email:
                    contact.email = self.email
                contact.text = self.text
            else:
                contact = ContactForm(**collect)
            contact.save()
            return redirect("list-contact")

Fungsi submit() standar gak ada yang aneh, untuk insert data jika tidak ada contact_id dan update jika ada.

Lalu di form-contact.html

<div>
    <!--Section: Contact v.2-->
    <section class="mb-4">

        <!--Section heading-->
        <h2 class="h1-responsive font-weight-bold text-center my-4">Contact us</h2>


        <div class="row">
            <!--Grid column-->
            <div class="col-md-12 mb-md-0 mb-5">
                

                    <!--Grid row-->
                    <div class="row">

                        <!--Grid column-->
                        <div class="col-md-6">
                            <div class="md-form mb-0">
                                <span class="error">{{ unicorn.errors.name.0.message }}</span>
                                <input unicorn:model="contact_id" type="hidden" id="id" name="id" class="form-control">
                                <input unicorn:model="name" type="text" id="name" name="name" class="form-control">
                                <label for="name" class="">Your name {{ name }}</label>
                            </div>
                        </div>
                        <!--Grid column-->

                        <!--Grid column-->
                        <div class="col-md-6">
                            <div class="md-form mb-0">
                                <span class="error">{{ unicorn.errors.email.0.message }}</span>
                                <input unicorn:model="email" type="email" id="email" name="email" class="form-control">
                                <label for="email" class="">Your email {{ email }} </label>
                            </div>
                        </div>
                        <!--Grid column-->

                    </div>
                    <!--Grid row-->

                    <!--Grid row-->
                    <div class="row">

                        <!--Grid column-->
                        <div class="col-md-12">

                            <div class="md-form">
                                <textarea unicorn:model="text" type="text" id="message" name="message" rows="2" class="form-control md-textarea"></textarea>
                                <label for="message">Your message</label>
                            </div>

                        </div>
                    </div>
                    <!--Grid row-->


                <div class="text-center text-md-left">
                    <button class="btn btn-success" unicorn:click="submit">Submit</button>
                    <a class="btn btn-warning" href={% url 'list-contact'%}>Cancel</a>
                </div>
                <div class="status"></div>
            </div>
            <!--Grid column-->

        </div>

    </section>
   
</div>

Di sini cukup to the point, di mana setiap field di html di atas binding dengan setiap variable di form-contact.py

Yang sedikit beda cara panggilnya

 {% unicorn 'form-contact' contact_id=id%}

Perintah di atas saat inisialisasi komponen akan sekalian mengirimkan nilai dari "contact_id".

Hasil akhirnya

Fungsi di atas langsung bekerja dengan baik kan termasuk validasi saat nulis email, itu dia akan langsung membaca ke django forms yang ditulis sebelumnya.

Dan ini hasil akhirnya

Sampai jumpa di tulisan lain lagi.