python - csv 上传时自定义 Django 管理页面出错(对象没有属性 'model' )

标签 python django csv django-admin

我一直在尝试在 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 portal.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 请求网址:http://127.0.0.1:8000/admin/portal/link/upload-csv/ Django 版本:3.2.4 异常类型:属性错误 异常值:
“CsvUploader”对象没有属性“model”

非常感谢任何帮助!

最佳答案

如果我正确理解这篇文章,我认为有一种更简单的方法可以在管理页面上创建上传/导入功能。

在您的管理模板中,您可以执行类似于以下操作的操作:

from django.contrib import messages, admin
from django.contrib.contenttypes.models import ContentType
from django.http import *
from django.shortcuts import redirect, render, HttpResponseRedirect
from django.urls import include, path, reverse
from portals.models import Links
from django.conf.urls import *
from django import forms
from portals.actions import export_as_csv_action

#Extend Admin portal use
class CsvImportForm(forms.Form):
    csv_upload = forms.FileField()

@admin.register(Links)
class LinksAdmin(admin.ModelAdmin):
    actions = [export_as_csv_action("Export Selected as CSV", fields=['name', 'url'], index=False)]

    def get_urls(self):
        urls = super().get_urls()
        new_urls = [path('upload-csv/', self.upload_csv),]
        return new_urls + urls

    def upload_csv(self, request):

        if request.method == "POST":
            csv_file = request.FILES["csv_upload"]
            
            if not csv_file.name.endswith('.csv'):
                messages.warning(request, 'The wrong file type was uploaded.')
                return HttpResponseRedirect(request.path_info)
            
            file_data = csv_file.read().decode("utf-8")
            csv_data = file_data.split("\n")

            for x in csv_data:
                flds = x.split(",")
                if x:
                    Links.objects.update_or_create(
                        name = flds[0],
                        url = flds[1],                    
                        )
                
            return redirect("..")
         
        form = CsvImportForm()
        data = {"form": form}
        return render(request, "admin/csv_upload.html", data)

**注意:上面没有验证,例如确保存在相同数量的列等。您也想这样做...

然后在模板文件夹中创建一个名为 admin 的文件夹,在其中创建另一个名为 Portals 的文件夹和一个名为 csv_upload.html 的文件 csv_upload.html 文件将如下所示:

{
  "%" "extends",
  "admin/base.html" "%"
}

{% block content %}
<div>
    <form action="." method="POST" enctype="multipart/form-data">
        {{ form.as_p }}
        {% csrf_token %}
        <button type='submit'>Upload File</button>
    </form>
    
</div>

{% endblock%}

然后在您的门户文件夹中创建一个 links 文件夹,并在该 links 文件夹中创建一个名为 change_list.html 的文件,如下所示:

{
  "%" "extends",
  "admin/change_list.html" "%"
}
{% load static %}
{% block content %}

<b><a href='upload-csv/'>Upload a CSV file.</a></b>

{{block.super}}

{% endblock %}

我已经对此进行了测试,它在我这边起作用了。

关于python - csv 上传时自定义 Django 管理页面出错(对象没有属性 'model' ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69592027/

相关文章:

python - 如何在 Django ModelAdmin 中的 “list_display” 显示 ForeignKey 字段的属性?

python - 使用 Python Pandas 将带逗号的字符串转换为数字

python - HBase-HappyBase : Socket Timeout Error For Larger Files - Works Good With Smaller one's

python - ctypes,函数返回指向结构的指针

python - 带有 Pandas 的 ipython 中的自动完成功能似乎已损坏

python - 如何使用 DateTimeField 和 input_formats 在 Django 模型表单中显示验证错误

javascript - 你能在 css 文档中使用 handlebars.js 变量吗?

python - 如何使用 Python basemap 更快地绘制地理定位的 RGB 数据

powershell - 根据行值过滤CSV文件

python - 如何通过XML解析并使用Python转换为CSV