【问题标题】:Django import-export - letting users import dataDjango import-export - 让用户导入数据
【发布时间】:2020-06-18 05:59:25
【问题描述】:

我正在尝试使用 django import-export 让用户导入自己的数据。我已经将它与管理员集成,效果很好,但我无法让用户导入端正常工作。

以下是我的看法:

from .models import WordResource
from tablib import Dataset
from .models import Word
from django.contrib import messages

# Word import
def import_words(request):
    if request.method == 'POST':
        file_format = request.POST['file-format']
        word_resource = WordResource()
        dataset = Dataset()
        new_words = request.FILES['importData']

        if file_format == 'CSV':
            imported_data = dataset.load(new_words.read().decode('utf-8'),format='csv')
            result = word_resource.import_data(dataset, dry_run=True, raise_errors=True)
        elif file_format == 'XLSX':
            imported_data = dataset.load(new_words.read(),format='xlsx')
            result = word_resource.import_data(dataset, dry_run=True, raise_errors=True)

        if result.has_errors():
            messages.error(request, 'Uh oh! Something went wrong...')

        else:
            # Import now
            word_resource.import_data(dataset, dry_run=False)
            messages.success(request, 'Your words were successfully imported')


    return render(request, 'vocab/import.html')

我的文字资源:

from import_export import resources
from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget

class WordResource(resources.ModelResource):
    target_word = Field(attribute='target_word', column_name='Russian')
    source_word = Field(attribute='source_word', column_name='Meaning')
    example_sentence = Field(attribute='example_sentence', column_name='Example sentence')
    fluency = Field(attribute='fluency', column_name='Fluency level')
    deck_name = Field(attribute='deck_name', column_name='Deck name')
    username = Field(attribute='username', column_name='username',widget=ForeignKeyWidget(User, 'username'))


    class Meta:
        model = Word
        fields = ("username", "target_word",'source_word','example_sentence',
        'fluency', 'deck_name',)
        import_order = fields
        skip_unchanged = True
        # exclude = ('id',)
        import_id_fields = ['username']

我的 Word 模型:

class Word(models.Model):
    target_word = models.CharField('Word in Russian',max_length=25,help_text="The word you want to add, in Russian")
    source_word = models.CharField('What does it mean?',max_length=25, help_text="Write down the translation in any language")
    add_to_word_list = models.BooleanField('Would you like to create a flashcard?', default=True)
    deck_name = models.CharField('Deck name', max_length=25)
    example_sentence = models.CharField(max_length=150,blank=True,help_text="It's important to learn words in context!")

    ## how well you know the word
    class Fluency(models.IntegerChoices):
        Beginner = 0
        Lower_intermediate = 1
        Upper_intermediate = 2
        Advanced = 3
    fluency = models.IntegerField(choices=Fluency.choices, help_text="How well do you know this word?",null=False)

    user = models.ForeignKey(User, related_name="words",on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.target_word

    def get_absolute_url(self):
        return reverse("vocab:detail",
        kwargs={"username": self.user.username, "pk": self.pk})

    class Meta:
        ordering = ["target_word"]

        constraints = [
            models.UniqueConstraint(fields=['user','target_word', 'source_word'],name='unique_word')]

还有我的 import.html 模板:

{% extends "vocab/vocab_base.html" %}
{% load bootstrap %}

{% block content %}

{% if messages %}
<div class="messages">
   {% for message in messages %}
   <h3  {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </h3>
   {% endfor %}
</div>

{% else %}

<h1> Import your words</h1>
<p>Here you can import your words from a csv or excel file.</p>

  <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      <input type="file" name="importData">
      <p>Please select the format of your file</p>
      <select name="file-format" class="form-control my-3">
          <option selected>Choose format...</option>
          <option>CSV</option>
          <option>XLSX</option>
        </select>
      <button class="btn btn-primary" type="submit">Import</button>
    </form>
  <a href="{% url 'vocab:index' %}">Back</a>
{% endif %}
  {% endblock %}

奇怪的是,当我尝试使用 csv 文件时,它似乎最初可以工作,但不能使用 xlsx 文件。然后,我只对代码的 xlsx 部分进行了更改。它不仅没有解决问题,现在 csv 也不起作用。

注意 - 这不是由于 id 列,因为我的测试上传文件中有一个空白的 id 列。

更新 - 我意识到我错过了用户字段,这是我的 Word 模型上的外键(非空键)。所以我添加了一个 ForeignKeyWidget,但我收到以下错误:NOT NULL constraint failed: vocab_word.user_id。即使我添加带有用户 ID 的列,错误仍然存​​在。我怎样才能解决这个问题?

我的 csv 文件如下所示:

username;Russian;Meaning;Example Sentence;Fluency level;Deck name
testuser;word1;word2;one two three;1;new

实施 Matthew 的更改后的回溯:

Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/vocab/import/

Django Version: 3.0.3
Python Version: 3.8.2
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'rest_framework',
 'bootstrap4',
 'bootstrapform',
 'languages',
 'django_countries',
 'import_export',
 'django_tables2',
 'django_filters',
 'accounts',
 'vocab',
 'flash',
 'api',
 'django_cleanup.apps.CleanupConfig']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\sqlite3\base.py", line 396, in execute
    return Database.Cursor.execute(self, query, params)

The above exception (NOT NULL constraint failed: vocab_word.user_id) was the direct cause of the following exception:
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\mvren\OneDrive\Documents\Coding\Russki\mysite\vocab\views.py", line 115, in import_words
    result = word_resource.import_data(dataset, dry_run=True, raise_errors=True)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\import_export\resources.py", line 627, in import_data
    return self.import_data_inner(dataset, dry_run, raise_errors, using_transactions, collect_failed_rows, **kwargs)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\import_export\resources.py", line 673, in import_data_inner
    raise row_result.errors[-1].error
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\import_export\resources.py", line 569, in import_row
    self.save_instance(instance, using_transactions, dry_run)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\import_export\resources.py", line 352, in save_instance
    instance.save()
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\base.py", line 745, in save
    self.save_base(using=using, force_insert=force_insert,
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\base.py", line 782, in save_base
    updated = self._save_table(
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\base.py", line 887, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\base.py", line 924, in _do_insert
    return manager._insert(
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\query.py", line 1204, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\models\sql\compiler.py", line 1384, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 100, in execute
    return super().execute(sql, params)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\mvren\miniconda3\envs\myRuEnv\lib\site-packages\django\db\backends\sqlite3\base.py", line 396, in execute
    return Database.Cursor.execute(self, query, params)

Exception Type: IntegrityError at /vocab/import/
Exception Value: NOT NULL constraint failed: vocab_word.user_id

【问题讨论】:

  • 在开发期间,您可以使用import_data(dataset, dry_run=True, raise_errors=True) 调用以使异常冒泡到调用过程。
  • 另外,您可以打印或记录 imported_data 以在运行导入之前查看数据集的外观。在与 Web 应用程序集成之前,您可以让 csv 导入在单独的脚本中“本地”工作。这可以缩短反馈循环并帮助您了解任何数据问题。
  • @MatthewHegarty 谢谢。我现在得到了一个NOT NULL constraint failed: vocab_word.user_id,即使我添加了一个带有用户 ID 的列,并且在添加了 ForeignKeyWidget 之后......你知道如何解决它吗?
  • 检查您是否引用了 csv 中的正确字段。检查它是否映射到模型上的属性。检查 csv 字段是否映射到 FK 关系中的正确字段(即您查找 FK 的哪个字段。例如:userid = fields.Field(column_name='user_id', attribute='user', widget=widgets.ForeignKeyWidget(User)。检查docs,如果您可以设置断点并逐步执行,您应该能够找到问题的根源。
  • 欢迎继续chat

标签: python html django xlsx django-import-export


【解决方案1】:

您的错误很清楚 - 无法创建对象,因为创建时 user_id 字段为空:

NOT NULL constraint failed: vocab_word.user_id

您的 csv 包含:

username;Russian;Meaning;Example Sentence;Fluency level;Deck name
testuser;word1;word2;one two three;1;new

您的Word 模型还定义了一个用户字段:

user = models.ForeignKey(User, related_name="words",on_delete=models.CASCADE)

这意味着当您在 django-import-export 中声明 Resource 时,您需要指定如何通过 FK 关系将 csv username 映射到任何现有的 user 实例。

您应该为此使用ForeignKeyWidget,因为它处理将 csv 字段映射到对象。

  • column_name 定义了我们用来查找用户引用的 csv 列
  • attribute 定义要设置的 Word 模型上的属性

此外,我们需要确保使用“正确”字段查找用户关系。来自docs

查找字段默认使用主键 (pk) 作为查找条件,但可以自定义为使用相关模型上的任何字段。

综上所述,我们的Field 定义如下所示:

userid = fields.Field(column_name='username', attribute='user', widget=widgets.ForeignKeyWidget(User, "username")

所以我认为您错误的根源是您错误地将attribute 设置为username。如果您调试代码,您可能会发现“testuser”User 实例已被加载,并分配给Word.username,这将被忽略,Word.user 将为空,因此出现错误。

更新

另一个需要纠正的问题:

fields 声明应引用要从 csv 数据设置的模型属性。

因此username 字段应该user,因为这是要更新的模型属性。

fields = ("user", "target_word",'source_word','example_sentence', 'fluency', 'deck_name',)

【讨论】:

  • 我仍然遇到同样的错误:(我将在我的问题中发布 Traceback。
  • 欢迎加入我的chat
  • 我不能在那个房间里打字。 chat.stackoverflow.com/rooms/216255/…>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-18
  • 2015-07-17
  • 1970-01-01
  • 1970-01-01
  • 2015-12-05
相关资源
最近更新 更多