Ошибка в пользовательской странице администратора 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
Вернуться на верх