Как преобразовать команды управления Django в действия формы, чтобы заполнить базу данных загруженным CSV-файлом?
Django 1.9.8, Python 3.6, база данных Postgres
Я пишу небольшое приложение на Django, которое будет использовать пользовательский интерфейс для импорта товаров из загруженного файла csv
через Form
и заполнения базы данных.
Я написал логику бэкенда для приложения, используя Custom Management Commands
, но не могу понять, как подключить его к фронтенду, используя Forms
.
Вот пример файла csv
:
name,sku,description
Brian James,skus-will-look-like-this,The products will have various descriptions. And multiple lines too.
У меня реализована следующая логика:
- Read a large
csv
file of 500K products to the database usingSKU
as a unique field. - When the file has uploaded, a live stream of what is happening should be displayed.
print()
currently serves this purpose but I think it should be possible to implement this withSSE
on the front end. - All products can be searched / filtered.
- Be able to delete all existing records and start a fresh upload as well as being able to add/update products manually.
Я создал простой загрузчик форм как отдельный проект, чтобы поиграть с ним, но даже после этого и чтения docs я не понимаю, как перейти от "преобразования" команд управления в жизнеспособные действия через Forms
.
Вот соответствующий код:
Это загружает локальный файл csv
, анализирует его, обновляет только определенные поля, если SKU
уже существует, создает статус 'активный/неактивный', и печатает после каждого сохранения, чтобы пользователь мог видеть, что происходит.
/products/management/commands/import_products.py
class Command(BaseCommand):
def handle(self, *args, **options):
print('Importing data from:', settings.DATA_IMPORT_LOCATION)
with open(f'{settings.DATA_IMPORT_LOCATION}/products.csv', 'r') as products_csv:
products_file = csv.reader(products_csv)
next(products_file) # skip header row
for counter, line in enumerate(products_file):
name = line[0]
sku = line[1]
description = line[2]
# Check if sku already exists if this just needs to be updated
if Product.objects.filter(sku=sku).exists():
sku_id = Product.objects.only('id').get(sku=sku).id
p = Product(id=sku_id)
p.name = name
p.description = description
p.status = random.choice(['active', 'inactive'])
p.save(update_fields=['name', 'description', 'status'])
print(p)
else:
p = Product()
p.name = name
p.sku = sku
p.description = description
p.status = random.choice(['active', 'inactive'])
p.save()
print(p)
print(f'Import complete, imported {counter} products')
Это удаляет все объекты из базы данных /products/management/commands/delete_products.py
from products.models import Product
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
Product.objects.all().delete()
models.py
class Product(models.Model):
name = models.TextField(blank=False, null=False)
sku = models.TextField(blank=False, null=False, unique=True)
description = models.TextField(blank=False, null=False)
status = models.TextField(blank=False, null=False, default='inactive')
def __str__(self):
return [self.name, self.sku, self.description, self.status]
Я должен быть в состоянии сделать что-то вроде этого:
Добавьте document = models.FileField(upload_to='documents/')
в models.py
Добавьте forms.py
from django import forms
from models import Product
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('name', 'sku', 'description', 'status', 'document')
/templates/products/model_form_upload.html
{% extends 'base.html' %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
<p><a href="{% url 'home' %}">Return home</a></p>
{% endblock %}
/templates/products/home.html
{% extends 'base.html' %}
{% block content %}
<ul>
<li>
<a href="{% url 'model_form_upload' %}">Model Form Upload</a>
</li>
</ul>
<p>Uploaded files:</p>
<ul>
{% for obj in products %}
<li>
<a href="{{ obj.product.url }}">{{ obj.product.name }}</a>
<small>(Uploading: {{ obj.name }})</small>
</li>
{% endfor %}
</ul>
{% endblock %}
Добавление этого в views.py:
def home(request):
products = Product.objects.all()
return render(request, 'products/home.html', { 'products': products })
def model_form_upload(request):
if request.method == 'POST':
form = ProductForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
else:
form = ProductForm()
return render(request, 'products/model_form_upload.html', {
'form': form
})
Добавление к urls.py:
urlpatterns = [
url(r'^$', views.home, name='home'),
url(r'^uploads/form/$', views.model_form_upload, name='model_form_upload'),
]
И добавляем это в settings.py:
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Добавление этого кода приводит к сбою приложения и многочисленным ошибкам (неправильно настроенные урлы, страница не загружается, данные не сохраняются и т.д.).
Я не уверен, как интегрировать Form
с логикой из Management Commands
так, чтобы csv
файл, загруженный через Form
действительно был "схвачен" и разобран, как это происходит в import_products.py -> with open(f'{settings.DATA_IMPORT_LOCATION}/products.csv', 'r') as products_csv:
Буду признателен за любую помощь или за направление к каким-либо источникам, которые рассматривают этот рабочий процесс CRUD операций с Forms
.