Ошибка в пользовательской странице администратора Django при загрузке csv (объект не имеет атрибута 'model')
Я пытался создать пользовательскую кнопку загрузки на странице администратора Django, но я продолжаю получать ошибку, указывающую на мой файл CsvUploader.py: object has no attribute 'model'
У меня есть очень простая модель:
class Link(models.Model):
name = models.CharField(db_column='Name', max_length=50)
url = models.URLField(db_column="URL", max_length=500)
def __str__(self):
return f"{self.name}: {self.url}"
Я также изменил свой шаблон администратора следующим образом:
#Extend Admin portal use
class CsvUploadAdmin(admin.ModelAdmin):
change_list_template = "custom_admin/csv_form.html"
def get_urls(self):
urls = super().get_urls()
additional_urls = [
path("upload-csv/", self.upload_csv),
]
return additional_urls + urls
urls = property(get_urls)
def changelist_view(self, request, extra_context=None):
extra = extra_context or {}
extra["csv_upload_form"] = CsvUploadForm()
return super(CsvUploadAdmin, self).changelist_view(request, extra_context=extra)
def upload_csv(self, request):
if request.method == "POST":
form = CsvUploadForm(request.POST, request.FILES)
if form.is_valid():
if request.FILES['csv_file'].name.endswith('csv'):
try:
decoded_file = request.FILES['csv_file'].read().decode('utf-8')
except UnicodeDecodeError as e:
self.message_user(
request,
"There was an error decoding the file:{}".format(e),
level=messages.ERROR
)
return redirect("..")
# Here we will call our class method
io_string = io.StringIO(decoded_file)
uploader = CsvUploader(io_string, self.model, portal)
result = uploader.create_records()
else:
self.message_user(
request,
"Incorrect file type: {}".format(
request.FILES['csv_file'].name.split(".")[1]
),
level=messages.ERROR
)
else:
self.message_user(
request,
"There was an error in the form {}".format(form.errors),
level=messages.ERROR
)
return redirect("..")
@admin.register(Link)
class LinkAdmin(CsvUploadAdmin):
pass
Наконец, вот мой файл CsvUploader.py:
import csv
from SecurityArchitecturePortal.models import Link
from django.db.utils import IntegrityError
from django.core.exceptions import FieldDoesNotExist
from django.db import transaction
from django.conf.urls import *
class CsvUploader:
def __init__(self, csv_file, model_name, app_name):
self.reader = list(csv.DictReader(csv_file, delimiter=','))
self.keys = [k for k in self.reader[0]]
self.model_fields = [f.name for f in self.model._meta.get_fields()]
self.valid = self._validate_csv()
self.csv_pos = 0
def _validate_csv(self):
keys = []
for k in self.keys:
if k.endswith("_id"):
keys.append(k[:-3])
else:
keys.append(k)
return set(keys).issubset(self.model_fields)
def read_chunk(self):
chunk = []
for i in range(1000):
try:
chunk.append(self.model(**self.reader[self.csv_pos]))
except IndexError as e:
print(e)
break
self.csv_pos += 1
return chunk
def create_records(self):
if not self.valid:
return "Invalid csv file"
while True:
chunk = self.read_chunk()
if not chunk:
break
try:
with transaction.atomic():
self.model.objects.bulk_create(chunk)
except IntegrityError as e:
for i in chunk:
try:
i.save()
except IntegrityError:
continue
print("Exception: {}".format(e))
return "records successfully saved!"
Я надеюсь получить второй набор глаз на это, который сможет заметить ошибку из строки 13 выше:
У объекта 'CsvUploader' отсутствует атрибут 'model'
Метод запроса: POST
URL запроса: http://127.0.0.1:8000/admin/portal/link/upload-csv/.
Версия Django: 3.2.4
Тип исключения: AttributeError
Значение исключения:
У объекта 'CsvUploader' нет атрибута 'model'
Любая помощь будет высоко оценена!
Проблема в том, что вы ожидаете, что self.model будет Link
объектом модели, потому что вы вызываете как показано ниже в вашем CsvUploadAdmin
:-
uploader = CsvUploader(io_string, self.model, portal)
что неправильно, у вас есть self.model
значение в переменной model_name, которое вы определяете в __init__
методе, например :-
def __init__(self, csv_file, model_name, app_name):
поэтому, чтобы решить вашу проблему, вам нужно добавить следующую строку в метод __init__
self.model = model_name
поэтому окончательный метод будет
def __init__(self, csv_file, model_name, app_name):
self.reader = list(csv.DictReader(csv_file, delimiter=','))
self.keys = [k for k in self.reader[0]]
self.model = model_name
self.model_fields = [f.name for f in self.model._meta.get_fields()]
self.valid = self._validate_csv()
self.csv_pos = 0