Django 导入导出 - 让用户导入数据
Django import-export - letting users import data
我正在尝试使用 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 列。
Update - 我意识到我缺少用户字段,它是我的 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
您的错误很明显 - 无法创建对象,因为 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
时,你需要指定 csv username
如何通过FK关系。
您应该为此使用 ForeignKeyWidget
,因为它处理将 csv 字段映射到对象。
column_name
定义我们用来查找用户引用的 csv 列
attribute
定义要设置的 Word
模型上的属性
此外,我们需要确保使用 "correct" 字段查找用户关系。来自 docs:
The lookup field defaults to using the primary key (pk) as lookup criterion but can be customised to use any field on the related model.
将它们放在一起,我们的 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',)
我正在尝试使用 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 列。
Update - 我意识到我缺少用户字段,它是我的 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
您的错误很明显 - 无法创建对象,因为 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
时,你需要指定 csv username
如何通过FK关系。
您应该为此使用 ForeignKeyWidget
,因为它处理将 csv 字段映射到对象。
column_name
定义我们用来查找用户引用的 csv 列attribute
定义要设置的Word
模型上的属性
此外,我们需要确保使用 "correct" 字段查找用户关系。来自 docs:
The lookup field defaults to using the primary key (pk) as lookup criterion but can be customised to use any field on the related model.
将它们放在一起,我们的 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',)