Search code examples
pythondjangodjango-modelsdjango-admindjango-admin-actions

Runing custom script (to populate model from CSV) from admin page


I am quite new to django and need help and ideas finding right way to do it. In short I have model which is populated few time a year from a CSV file (deleting all info and replacing it by new form file). To do that - I wrote script which is run from shell. It works, but not convenient and I cannot let to do it someone else. I would like to upgrade and make that script callable from admin page. This is what got so far:

To upload CSV file I created model for it (in models.py):

class DrgDuomenys(models.Model):

    drg_duomenys = models.FileField(upload_to='DRG_csv/')
    data = models.DateField(auto_now=False, auto_now_add=True)

Model which should be populated (also in models.py):

class DRGkodas(models.Model):

    drg_kodas = models.CharField(max_length=4)
    drg_koeficientas = models.DecimalField(max_digits=6, decimal_places=3)
    drg_skaicius = models.IntegerField()

    def __str__(self):
        return self.drg_kodas

Old script (named ikelti_drg.py) for populating model, activated from shell by writing py manage.py ikelti_DRG --path path/to/your/file.csv

import csv from django.core.management import BaseCommand from stv.models import DRGkodas, DrgDuomenys

class Command(BaseCommand):

    def add_arguments(self, parser):
        parser.add_argument('--path', type=str)

    def handle(self, *args, **kwargs):
        path = kwargs['path']
        with open(path, 'rt') as f:
            reader = csv.reader(f, dialect='excel')
            for row in reader:
                drg_kodai = DRGkodas.objects.create(
                    drg_kodas = str(row[0]),
                    drg_koeficientas = float(row[1]),
                    drg_skaicius = int(row[2]),
                )

I tried edit it to use CSV from database to simplify process, so my edited script which should be callable from admin page (probably incorrect, so any help here would be also appreciated):

import csv
from django.core.management import BaseCommand
from stv.models import DRGkodas, DrgDuomenys

class LoadDrg(BaseCommand):
    #Load DRG duomenų csv file into the database

    data = DrgDuomenys.objects.latest('data')
    data_file = data.drg_duomenys 

    def handle(self, *args, **kwargs):
        with open(data_file) as f:
            reader = csv.reader(f, dialect='excel')
            for row in reader:
                drg_kodai = DRGkodas.objects.create(
                    drg_kodas = str(row[0]),
                    drg_koeficientas = float(row[1]),
                    drg_skaicius = int(row[2]),
                )

Lastly I amended registered DRGkodas module in admin.py to include new-baked action like this:

from stv.management.commands.ikelti_DRG_admin import LoadDrg
class DRGkodaiAdmin(admin.ModelAdmin):
    list_display = ('drg_kodas', 'drg_koeficientas', 'drg_skaicius')
    actions = ['paskelbti_drg_info',]

    def paskelbti_drg_info(self, request):
        uzpildyti_drg = [LoadDrg]
    paskelbti_drg_info.short_description = "Upload new info"   
admin.site.register(DRGkodas, DRGkodaiAdmin)

Now the problem is, that to call action I have to choose at least one entry (and there should be none, since I am trying to create them) also if I create dummy entry and choose it to make command run, it (obviously) do not work.

My main question would be what - correct way to do it? At first I imagined that I should put button somewhere on admin page, but after some research I did not find anything solid on how to do that. So I tried to add action to model directly (my code above), but that work little bit different than I need.

Please give me some advise on what approach I should use, also any literature or example code snippet would be very appreciated.


Solution

  • To do things like these I use a button in the admin. To do that override the coresponding admin template (e.g. base_site.html if you want the button on the top) and add there something like:

    <form action="/action_view" style="display:inline-block;">
        <input type="submit" value="Upload Data" />
    
    </form>
    

    Then create the action_view and hook it up with your function in the url.

    views.py:

    def action_view(request):
        with open(data_file) as f:
            reader = csv.reader(f, dialect='excel')
            for row in reader:
                drg_kodai = DRGkodas.objects.create(
                    drg_kodas = str(row[0]),
                    drg_koeficientas = float(row[1]),
                    drg_skaicius = int(row[2]),
                )
    

    Add to url.py:

    url(r'^action_view/$', action_view, name="action_view"),