How to create a button to change a text in my html with data coming from Django’s models.py?

Asked

Viewed 54 times

-2

I’m starting to work with Django and I’m starting a test to solidify what I’ve been learning. The idea is a single page, which displays a phrase as soon as the site opens. Below the phrase, there is a button that I would like to change the phrase to some other phrase coming from a variable declared in models.py and that contains several phrases that have been logged through Django’s admin panel.

This is my models.py file:

from django.db import models


class Base(models.Model):
    criado = models.DateField('Criado', auto_now_add=True)
    modificado = models.DateField('Atualização', auto_now=True)
    ativo = models.BooleanField('Ativo', default=True)

    class Meta:
        abstract = True


class Frase(Base):
    frase = models.CharField('Frase', max_length=100)
    dica = models.CharField('Dica', max_length=200, default='-')

    class Meta:
        verbose_name = 'Frase'
        verbose_name_plural = 'Frases'

    def __str__(self):
        return self.frase

This is my views.py file:

from django.views.generic import TemplateView
from .models import Frase


class IndexView(TemplateView):
    template_name = 'index.html'

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        context['frases'] = Frase.objects.order_by('?').all()

        return context

This is my index.html:

            <div class="container px-4 px-lg-5 h-100">
                <div class="row gx-4 gx-lg-5 h-100 align-items-center justify-content-center text-center">
                    <div class="col-lg-8 align-self-end">
                        <h1 class="text-white font-weight-bold" id="frase">{{ frases|first }}</h1>
                        <hr class="divider" />
                    </div>
                    <div class="col-lg-8 align-self-baseline">
                        <a class="btn btn-primary btn-xl" onclick="nova_frase()">Nova frase</a>
                    </div>
                </div>
            </div>
     (...) <!--rest of the html code-->
        
         <script type="text/javascript">
            function nova_frase() {
                document.getElementById('frase').innerText = '{{ frases|random }}';
            }
        </script>

I’m having trouble finding texts to do this task (click the button and change the phrase that is being displayed by another sentence coming from the variable defined in models.py, without having to refresh the page).

I am using Python 3.9.5 and Django 3.2.5.

If anyone can help me, I’d be very grateful.

1 answer

-1


When you send the context to Django’s front-end, it happens only once. To get all the phrases, you could loop the template and put each phrase into an html element (div, p, or whatever you want), and these elements can be created as display None with a common css class for everyone, and be managed by javascript.

In html:

{% for frase in frases %}
<p class='hide'>{{ frase }}</p>
{% endfor %}

In the JS:

let button = document.getElementById('seu-botao')//pega o botão e adiciona um listener de click
button.addEventListener('click',function(){
    let fraseAtual = document.getElementsByClassName('show')[0]//Pega frase atual
    fraseAtual.classList.remove('show') // Limpa a frase atual, para iniciar um novo sorteio
    fraseAtual.classList.add('hide') // Limpa a frase atual, para iniciar um novo sorteio
    let frases = document.getElementsByClassName('hide');//Pega todas as frases invisíveis, como são várias, o retorno é semelhante a um array.
    let sorteio = Math.floor(Math.random() * frases.length);//Sorteia um index do "array"
    let frase_visivel = frases[sorteio].classList.remove('hide')// Pega a frase do index sorteado e remove a classe css invisível
    frase_visivel.classList.add('show')//Adiciona classe visível.
  
})

In css:

.hide{
  display:none;
}
.show{
  display:block;
}

Let’s review what happened in each line of the Istener:

(I’ll skip the first two to explain later.)

let frases = document.getElementsByClassName('hide')

Here we get all the phrases by the css class common to all. The return is almost like an array, so each element can be returned by calling the index.

let sorteio = Math.floor(Math.random() * frases.length)

To draw an index, we use Random. Here it will return a number between 0 and the list size minus 1.

let frase_visivel = frases[sorteio].classList.remove('hide')
frase_visivel.classList.add('show')

Here we take the selected index and rescue our phrase by removing the invisible class and adding the visible class.

Now, back to the beginning, in these two lines, we are just preparing the code to clear our Gift, and allow for another draw with all the phrases, and preventing the phrases from piling up on the screen.

let fraseAtual=document.getElementsByClassName('show'[0].classList.remove('show')
fraseAtual.classList.add('hide')

To finish I leave here 3 suggestions:

  1. You can, instead of doing this above, use AJAX, to give dynamism to the page, you can see a little more about ajax on this issue of stackoverflow.

  2. If you prefer, instead of removing and adding classes, you can simply switch, with the classList itself, if you don’t know the classList read that documentation.

  3. To work with randomness in javascript you can try using this little module, made by me. If you want to contribute too, feel free.

  • Thank you very much! I had not known how to do this for a while, without having to interrupt my Django course in half so I could study Javascript and the like. And the fact that I couldn’t find any answers that would help me, and no answer here was getting me down enough. I had to make small changes to the code you sent, because I was using Bootstrap. Instead of creating the class Hide and show, used d-None and d-block to change the display of sentences. let fraseAtual = document.getElementsByClassName('show')[0].classList.remove('show') make it work. +

  • I had to "break" in two lines. : let frase_atual = document.getElementsByClassName('d-block')[0] E in the following row: frase_atual.classList.remove('d-block') Leaving everything in just one line, I had the following error: Uncaught Typeerror: Cannot read Property 'classList' of Undefined at Htmlanchorelement.<Anonymous>. After a while thinking about, and rewriting the code, I found the solution of splitting it into two lines of code. I would like to ask about performance if there are many sentences (for example 1000). This solution would leave the page "heavy"?

  • Dude, I don’t know if this is exactly harmful, but even though it’s something native to Django, carrying TOO much can be tricky. Look about AJAX, as I said in the reply. This can prevent your page from having to load all the phrases at once. I will adjust the answer.

  • I get it, @Rubens . I’m really going to look for AJAX to optimize this. But for now, this solution has allowed me to complete this little project and I can now continue the rest of the course. But again, thank you so much for being willing to help. In addition, I saw the module you created and I found too much mass! I hope to use it in other projects. Hugs. If you want to see the project I built, it’s in Heroku at: link

Browser other questions tagged

You are not signed in. Login or sign up in order to post.