[Python][Django] Form field berekenen tijdens het invullen

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • Yukkie
  • Registratie: Januari 2001
  • Laatst online: 22:44

Yukkie

Vorsprung Durch Technik

Topicstarter
Enorm enthousiast ben ik onlangs begonnen met een klein Django projectje om het brandstofgebruik van mijn auto vast te leggen. Voorheen deed ik dat in Excel, maar ik wil dit via een vpn en mijn telefoon direct in een DB stoppen op mijn NAS.

In principe ben ik een heel eind, alleen heb ik nog een paar wensen waar ik nog niet helemaal uitkom, mede misschien omdat ik de juiste terminologie niet weet te vinden om op te zoeken met Google.

Ik heb het volgende model:
Python: models.py
1
2
3
4
5
6
7
class fuel_stats(models.Model):
    date = models.DateField(("Date"), default=date.today)
    mileage = models.PositiveIntegerField()
    liter = models.DecimalField(max_digits=5, decimal_places=2)
    price_liter = models.DecimalField(max_digits=5, decimal_places=3)
    trip = models.PositiveSmallIntegerField()
    total_cost = models.DecimalField(max_digits=5, decimal_places=2)

n mijn formulier wil ik eigenlijk (net als in mijn Excel sheetje) alleen de datum aanpassen eventueel, de kilometerstand, het aantal getankte liters en de literprijs in willen vullen. Op basis van die gegevens en de KM-stand van de vorige tankbeurt wil ik dat de trip (mileage - vorige mileage) wordt berekend en de total_cost op basis van de liters en liter prijs.

Dat laatste heb ik al min of meer voor elkaar doordat het berekend wordt bij het submitten van het formulier:
Python: models.py
1
2
3
4
5
6
7
    def get_computed(self):
        result = self.liter * self.price_liter
        return result

    def save(self, *args, **kwargs):
        self.total_cost = self.get_computed()
        super(fuel_stats, self).save(*args, **kwargs)

Maar eigenlijk wil ik het zichtbaar hebben als ik het formulier invul. Voorlopig voldoet het wel, maar het zou mooi zijn als de berekening in het formulier zichtbaar is, zodat ik het evt kan corrigeren, mocht het nodig zijn.

Daarnaast ben ik op zoek naar hoe ik de trip afstand bereken.
In principe moet denk ik ik bij het laden van de data voor het formulier de laatste mileage ophalen. Dat is op zich gemakkelijk, want dat is ook altijd de hoogste value van die column in principe (tenzij ik ga sjoemelen met mijn km-teller :P ) maar dan? Ik had al zitten denken aan een oplossing met javascript, maar hoe en waar voeg ik dat in in mijn form? Het forumulier wordt dynamisch opgebouwd en ik vind de validaties dmv boodstrap wel erg tof. Daarnaast vraag ik me af hoe ik dan in het javascriptje kan verwijzen naar velden in het formulier, als die helemaal nog niet bestaan als het formulier nog gegenereerd moet worden.

HTML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<form method="POST" class="post-form" id="new_fuel" novalidate>{% csrf_token %}
  {% for field in form %}
    <div class="form-group">
      {{ field.label_tag }}

      {% if form.is_bound %}
        {% if field.errors %}

          {% render_field field class="form-control is-invalid form-control-lg" %}
          {% for error in field.errors %}
            <div class="invalid-feedback">
              {{ error }}
            </div>
          {% endfor %}

        {% else %}
          {% render_field field class="form-control is-valid form-control-lg" %}
        {% endif %}
      {% else %}
        {% render_field field class="form-control form-control-lg" %}
      {% endif %}

      {% if field.help_text %}
        <small class="form-text text-muted">
          {{ field.help_text }}
        </small>
      {% endif %}
    </div>
  {% endfor %}
  <button type="submit" class="btn btn-primary form-control-lg">Save</button>
</form>

Dit formulier heb ik gemaakt mbv deze tutorial: https://simpleisbettertha...ies/beginners-guide/1.11/

Ik ben nu al 2 avondjes aan het zoeken, maar ik zit even vast. Kan iemand mij weer even op weg helpen?

[ Voor 6% gewijzigd door Yukkie op 05-02-2019 21:28 ]

We've got that ring of confidence

Beste antwoord (via Yukkie op 05-02-2019 23:56)


  • MaNDaRK
  • Registratie: Oktober 2001
  • Laatst online: 19:49
Je hoeft niet perse de onChange te gebruiken van JavaScript. Je kan eventueel hiervoor ook jQuery - change voor gebruiken zie: https://api.jquery.com/change/
Let hierbij wel op is dat change alleen wordt geactiveerd nadat het veld de focus heeft verloren. Wil je het tijdens het typen doen zou ik kijken naar bijvoorbeeld keyup - http://api.jquery.com/keyup/

Mocht je puur JavaScript willen gebruiken, kan je ook je forms handmatig renderen. Maar aangezien je Bootstrap gebruikt lijkt me dat niet nodig ;)

[ Voor 20% gewijzigd door MaNDaRK op 05-02-2019 22:31 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • MaNDaRK
  • Registratie: Oktober 2001
  • Laatst online: 19:49
Ik denk dat je dit op twee manieren kan aanpakken, de berekening van de 'total_cost' zou ik met een stukje JavaScript doen.

En als je in dit voorbeeld kan zien kan je een formulier ook vooraf invullen (prepopulate). Deze data kan data zijn uit je 'fuel_stats' model zijn.
Python:
1
2
from django.db.models import Max
maxmilage = fuel_stats.objects.all().aggregate(Max('price'))
Voor meer info zie: https://docs.djangoproject.com/en/2.1/topics/db/aggregation/

En als de velden in het formulier staan, kan je ze met een stukje JavaScript uitlezen, aanpassen en in een ander element terug plaatsen.
Door het 'element' te inspecteren kan je er achter komen wat de selector is voor het object.

En ik leer ook nog eens wat: met de @property method decorator ook een 'calculated' field maken. Zie https://www.reddit.com/r/.../calculated_model_fields/

[ Voor 27% gewijzigd door MaNDaRK op 05-02-2019 22:18 ]


Acties:
  • 0 Henk 'm!

  • Yukkie
  • Registratie: Januari 2001
  • Laatst online: 22:44

Yukkie

Vorsprung Durch Technik

Topicstarter
Je bedoelt dus dat ik het formulier prepopulate met het veld maxmilage?
Dat is inderdaad een deel van de oplossing (thanks!)

Ik had inmiddels al een simpel javascriptje gemaakt voor het berekenen van de price_total, maar ik zit nog even te stoeien met hoe ik dat afvuur. Het mooiste zou zijn dat een wijziging van het liter en/ofof liter_price veld het script je triggered. alleen moet ik daarvoor een onChange="calc()" tag toevoegen aan die twee velden in het formulier. Maar hoe krijg ik dat voor elkaar? Ik had het al wel gevonden hoe je dat kan doen als je je form niet op een model baseert, maar mijn forms.py ziet er zo uit:

Python: forms.py
1
2
3
4
5
6
7
8
from django import forms
from .models import fuel_stats

class NewFilling(forms.ModelForm):

    class Meta:
        model = fuel_stats
        fields = ('date', 'mileage', 'liter', 'price_liter', 'trip')

Hoe ga ik daar nou (extra) HTML-tags aan hangen?

We've got that ring of confidence


Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • MaNDaRK
  • Registratie: Oktober 2001
  • Laatst online: 19:49
Je hoeft niet perse de onChange te gebruiken van JavaScript. Je kan eventueel hiervoor ook jQuery - change voor gebruiken zie: https://api.jquery.com/change/
Let hierbij wel op is dat change alleen wordt geactiveerd nadat het veld de focus heeft verloren. Wil je het tijdens het typen doen zou ik kijken naar bijvoorbeeld keyup - http://api.jquery.com/keyup/

Mocht je puur JavaScript willen gebruiken, kan je ook je forms handmatig renderen. Maar aangezien je Bootstrap gebruikt lijkt me dat niet nodig ;)

[ Voor 20% gewijzigd door MaNDaRK op 05-02-2019 22:31 ]


Acties:
  • 0 Henk 'm!

  • Yukkie
  • Registratie: Januari 2001
  • Laatst online: 22:44

Yukkie

Vorsprung Durch Technik

Topicstarter
Nee, ik zit niet vast aan JavaScript, maar jQuery is nog geheel onontdekt en onontgonnen gebied voor me ;) ik ga me daar morgen eens in verdiepen. Thanks zover, ik laat het weten als het gelukt is!

edit: En dat is dus nu :)

voor de volledigheid, Ik heb het zo opgelost:

in views.py heb ik dit toegevoegd:
code:
1
maxmileage = fuel_stats.objects.all().aggregate(Max('mileage'))


en dit is de jQuery code die ik toegevoegd heb aan mijn template:
HTML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
  function calc_filling_cost(){
    var liter = $('input[name="liter"]').val();
    var price = $('input[name="price_liter"]').val();
    var price_total=(liter*price).toFixed(2);
    $('input[name="total_cost"]').val(price_total);
  }

  $(function() {
    //when #liter_price input is changed
    $("input[name='price_liter']").change(
      function(){
        calc_filling_cost();
      });
  });

  function calc_trip(){
    var prev_mileage = "{{ maxmileage.mileage__max }}";
    var new_mileage = $('input[name="mileage"]').val();
    var new_trip = new_mileage-prev_mileage;
    $('input[name="trip"]').val(new_trip);
  }

  $(function(){
    $("input[name='mileage']").change(
      function(){
        calc_trip();
      });
  });
</script>

mocht iemand tips hebben voor het verbeteren van mijn jQuery code, dan hoor ik het graag!

Het bijkomende voordeel is dat ik nog steeds controle heb over de invoer: Als het totaal bedrag door een afronding toch anders is of door een verschil in 0.1KM's de trip net afwijkt, dan kan ik dat altijd nog corrigeren bij het invoeren.

Nogmaals bedankt voor je hulp!

[ Voor 80% gewijzigd door Yukkie op 06-02-2019 00:03 ]

We've got that ring of confidence