How do I create a rate calculator in django?

Basically I am creating a rate calculator for a utility company in my country based on the progressive rate table below example

2. (a) For one month less than 1,001 gallons will be charged .................. $ 9.23

(b) Proportional duty per gallon thereafter up to and including 2,000 gallons at the rate per 1,000 gallons .................. $ 15.89

(c) Proportional duty per gallon in excess of 2,000 gallons and up to and including 3,000 gallons at the rate of 1,000 gallons .... $ 17.43

(d) Proportional duty for each gallon in excess of 3,000 gallons and up to and including 4,000 gallons at the rate of 1,000 gallons .... US $ 18.45

(e) Proportional charge for each gallon in excess of 4,000 gallons and up to and including 5,000 gallons at the rate of 1,000 gallons .... $ 19.48

(f) Proportional duty for each gallon in excess of 5,000 gallons and up to and including 6,000 gallons at the rate of 1,000 gallons .... $ 20.50

(g) Proportional charge for each gallon in excess of 6,000 gallons and up to and including 7,000 gallons at the rate of 1,000 gallons .... $ 21.01

(h) Proportional duty per gallon above 7,000 gallons and up to and including 8,000 gallons at the rate per 1,000 gallons ............ US $ 21.53

(i) Proportional duty per gallon above 8,000 at the rate per 1,000 gallons ... $ 22.04

I am thinking about creating a Rate model with the following fields:

  • delivery area
  • Description
  • from_gallons (initial gallons range)
  • to_gallons (end gallons range)
  • rate_per_1000g

my opinion will take a post from the form with gallons, delivery area and should return the usage calculated from the rate table.

i would do something like this in the view

from models import Rate    
def tarrif_calc(request,gallons,area):
    rate = Rate.objects.get(area_of_supply=area, from_gallons >= gallons, to_gallons <= gallons)
    rate_per_gal = rate.rate_per_1000g/1000
    usage = gallons * rate_per_gal
    return usage

      

This is the simplest one I can think of, however I am not sure how I would handle progressive bets ... any ideas? Am I on the right track?

0


a source to share


2 answers


Business rules must be expressed in code. Additional Information

After reading your question a little closer. It looks like you might have a lot of such tables that you have to query. Putting many such structures legally goes into the database. For now, I assume you have it. If you really only have one table, the case analysis is probably the most concise.

In my own django applications, I've found that a form is a very natural place to put business logic. Models seem obvious, but rarely have access to the context that is needed to make informed decisions about what might go there.

I usually divide it into several dependent applications. One application provides very high level, abstract models and forms. Another application extends the forms from the former to business rules.

The first problem is requesting the Rate model. The bit of code for this doesn't really work. You cannot go through lazy comparisons in python. Django provides a mechanism for this by changing the names of the arguments.

rate = Rate.objects.get(area_of_supply=area, from_gallons__gte=gallons, to_gallons__lte=gallons)

      

I'm not sure what your db looks like, but make sure there is a pointer to from_gallons and to_gallons, or this query will be very slow.

Your example doesn't sound like it changes state. If so, then you should probably use the GET view and not POST.

In any case, the first part of the logic is the same. Receive a request and submit the form.



# in "myproject/businessrules/forms.py"
from django import forms
from myproject.otherapp.models import Area

class RateForm(forms.Form):
    # declare as instance variables the data actually posted
    quantity = forms.DecimalField(label="amount of gallons")
    area = forms.ModelChoiceField(label="area of supply", queryset=Area.objects.all() )
    # now add the business rule!
    def clean(self):
        cleaned_data = self.cleaned_data # quantity and area area already validated 
        rate = Rate.objects.get(
                            area_of_supply=cleaned_data["area"],
                            from_gallons__gte=cleaned_data["gallons"],
                            to_gallons__lte=cleaned_data["gallons"])
        usage = cleaned_data["gallons"] * rate.rate_per_1000g/1000
        cleaned_data["usage"] = usage
        return cleaned_data

      

Now all that is needed is to actually use it.

# in "myproject/myapp/views.py"
from django.shortcuts import render_to_response
from businessrules import RateForm

def tarrif_view(request):
    form = RateForm(request.GET)
    if form.is_valid():
        context = { "form": form, "valid": True, "usage":form.cleaned_data["usage"] }
    else:
        context = { "form": form, "valid": False }
    return render_to_response("tarrif.html", context)

      

There is very little business knowledge embedded in the view. Just the fact that the form stores the cleared calculation result in its "use" key. But since the presentation is designed specifically to capture that value, it's done right.

A template is also required to use the form. (My skill for the django template is just that. As I said, I prefer jinja2 and you should too)

Personally, I don't really like the guesswork of moving individual form results into the template context, I just use the entire processed form. I write my own templates in jinja2, although if your template designers are not trustworthy, you may need to handle your contexts a little more carefully than this.

{# in "myproject/templates/tarrif.html" #}
{% if valid %}
Usage: {{ usage }} dollars.
{% else %}
<form action="" method="GET">
    {{ form }}
    <input type="submit" \>
</form>
{% endif %}

      

Again, there is no more business logic in the template than in the view.

+1


a source


The most complete example is now from @TokenMacGuy above. and I understand what you mean in the business logic in the form. Though I think you weren't considering progressive rates that would have made it even longer lasting.

I am answering my own question after you completed this issue, and in the hope that someone else can inject and educate django users about business logic cases.

so this is what i did:

first created a betting model to be stored in the database. I have marked each speed with a speed level to be able to handle progressive grading.

class Rate(models.Model):
    area_of_supply = models.ForeignKey(SupplyArea)
    description = models.TextField()
    rate_level = models.CharField(max_length=2,choices=RATE_LEVEL_CHOICES,)
    rate_per_1k_gallons = models.DecimalField(max_digits=10,decimal_places=2)

      

Then I created in my views.py a view that will handle the request from the form (in my case both in ajax and normal form). Like "business logic", in my case "traffic_calc ()"



def tariff(request):
    if request.method == 'POST':
        form = TariffForm(request.POST)
        if form.is_valid():
            gallons = form.cleaned_data['gallons']
            gallons = int(gallons)
            area_id = form.cleaned_data['tariff']
            area = area_id.name
            usage = tarrif_calc(request, area_id, gallons)    
            response_dict = {'gallons': gallons,'area': area,'usage': usage}
            if request.is_ajax():
                return HttpResponse(simplejson.dumps(response_dict), mimetype='application/json')
            return render_to_response('rates/tariff_form.html', response_dict)
    else:
        form = TariffForm()
    return render_to_response('rates/tariff_form.html', {'form': form})

def tarrif_calc(request, area, gallons):
    r_levels = gallons//1000
    remainder = gallons%1000
    usage = 0
    i = 1

    if r_levels < 1:
        rate = Rate.objects.get(area_of_supply=area,rate_level=1)
        usage = rate.rate_per_1k_gallons    
    else:
        if r_levels >= 8:
            while i <= 9:
                rate = Rate.objects.get(area_of_supply=area,rate_level=i)
                usage += rate.rate_per_1k_gallons
                i += 1

            if gallons > 9000:
                remainder = gallons - 9000
            remainder_rate = Rate.objects.get(area_of_supply=area,rate_level=9)
            usage += remainder * remainder_rate.rate_per_1k_gallons/1000
            usage = round(usage,2)
        else:    
            while i <= r_levels:
                rate = Rate.objects.get(area_of_supply=area,rate_level=i)
                usage += rate.rate_per_1k_gallons
                i += 1
            remainder_level = r_levels+1
            if remainder_level >= 9:
                remainder_rate = Rate.objects.get(area_of_supply=area,rate_level=9)
            else:
                remainder_rate = Rate.objects.get(area_of_supply=area, rate_level=remainder_level)    
                usage += remainder * remainder_rate.rate_per_1k_gallons/1000
                usage = round(usage,2)
    return usage

      

I agree with @TokenMacGuy .. I should have probably used GET, but used POST, because if js is not available the regex works just as well. I also agree that at this level @TokenMacGuy can probably only import tariff_scal from views and use it in their .py forms.

Then I created the required forms and templates and javascript that handles the ajax posting (although not required)

// JavaScript Document
$(document).ready(function()    {

$('#tariff_form').submit(function(event){
    event.preventDefault();  // stop the form from submitting and refreshing the page
    var form = this;               // in jQuery, this refers to the current element, which is #article_form

    // grab each form value
    var data = {};
    gallons = $('#id_gallons').val();
    tariff = $('#id_tariff').val();

    // now send the data to the server via AJAX
    if(gallons && tariff)
    {
        $.ajax({
            type: "POST",
            url: "/tariff-calculator/",
            data: "gallons="+gallons+"&tariff="+tariff,
            dataType: "json",
            success: function(json){
                $('#gallons_cont').html(json['gallons']);
                $('#area_cont').html(json['area']);
                $('#usage_cont').html(json['usage'])
                $('#results_json').show('slow');            
            },
            });
    }
    else
    {
        alert('Please Fill All Fields')
    }
}); 

});

      

I guess both methods are similar and both work (although I haven't tried @ TokenMacGuy's), except for the tariff_calc () function, it seems that MackGuy solved the problem, so I'll give it the correct answer, although I would like more comments on business logic in forms.py as I am not completely convinced or think there is a huge difference.

0


a source







All Articles