如何唯一标识django中的模型实例
How to uniquely identify a model instance in django
我有一个程序可以保存不同学校的图书记录我有一个学生模型,可以让每所学校从 excel 上传学生。然而,我得到一个错误,因为已经有两所学校分别有 form 1 form 2 和 form 3。它 returns MultipleObjectsReturned 错误。
将字段设置为唯一也不允许其他学校创建相同的类。
我如何能够唯一地识别每个实例 ok Klass 模型,所以它 returns 没有错误。
get() 返回了不止一个 Klass -- 它返回了 2 个!
class ImportStudentsResource(resources.ModelResource):
school = fields.Field(attribute = 'school',column_name='school', widget=ForeignKeyWidget(School, 'name'))
klass = fields.Field(attribute = 'klass',column_name='class', widget=ForeignKeyWidget(Klass, 'name'))
stream = fields.Field(attribute = 'stream',column_name='stream', widget=ForeignKeyWidget(Stream, 'name'))
class Meta:
model = Student
fields = ('school','student_id','name','year','klass','stream')
import_id_fields = ('student_id',)
import_order = ('school','student_id','name','year','klass','stream')
class uploadStudents(LoginRequiredMixin,View):
context = {}
def get(self,request):
form = UploadStudentsForm()
self.context['form'] = form
return render(request,'libman/upload_student.html',self.context)
def post(self, request):
form = UploadStudentsForm(request.POST , request.FILES)
data_set = Dataset()
if form.is_valid():
file = request.FILES['file']
extension = file.name.split(".")[-1].lower()
resource = ImportStudentsResource()
if extension == 'csv':
data = data_set.load(file.read().decode('utf-8'), format=extension)
else:
data = data_set.load(file.read(), format=extension)
result = resource.import_data(data_set, dry_run=True, collect_failed_rows=True, raise_errors=True)
if result.has_validation_errors() or result.has_errors():
messages.success(request,f'Errors experienced during import.')
print("error", result.invalid_rows)
self.context['result'] = result
return redirect('upload_students')
else:
result = resource.import_data(data_set, dry_run=False, raise_errors=False)
self.context['result'] = None
messages.success(request,f'Students uploaded successfully.')
else:
self.context['form'] = UploadStudentsForm()
return render(request, 'libman/upload_student.html', self.context)
class Student(models.Model):
school = models.ForeignKey(School, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
now = datetime.datetime.now()
YEAR = [(str(a), str(a)) for a in range(now.year-2, now.year+2)]
year = models.CharField(max_length=4, choices = YEAR)
student_id = models.CharField(max_length=20,unique=True)
klass = models.ForeignKey(Klass,on_delete=models.CASCADE)
stream = models.ForeignKey(Stream,on_delete=models.CASCADE)
导入数据时,逻辑无法唯一标识 'klass' 的唯一实例。您正在使用与 class 的外键关系的 'name' 属性,但是正如您所说,此 'name' 字段无法唯一标识 Klass 的实例,因此您得到 MultipleObjectsReturned
错误。
解决方案是创建 ForeignKeyWidget
的子class,它使用可以唯一标识 Klass 的覆盖 get_queryset()
。您将需要使用可以添加到查询中的附加参数。我猜 school_id
可能有效(但我不知道你的数据模型)。
class KlassForeignKeyWidget(widgets.ForeignKeyWidget):
"""
Custom widget to lookup a Klass instance
"""
def __init__(
self,
school_id,
field="name",
*args,
**kwargs,
):
super().__init__(self.model, field=field, *args, **kwargs)
self.school_id = school_id
def get_queryset(self, value, row, *args, **kwargs):
return self.model.objects.filter(school_id=self.school_id)
然后您必须在您的资源中声明此自定义小部件:
class ImportStudentsResource(resources.ModelResource):
def __init__(self, school_id):
super().__init__()
self.school_id = school_id
self.fields["klass"] = fields.Field(
attribute="klass",
column_name="class",
widget=myapp.widgets.KlassForeignKeyWidget(
school_id,
),
)
然后当你实例化 ImportStudentsResource
.
我有一个程序可以保存不同学校的图书记录我有一个学生模型,可以让每所学校从 excel 上传学生。然而,我得到一个错误,因为已经有两所学校分别有 form 1 form 2 和 form 3。它 returns MultipleObjectsReturned 错误。 将字段设置为唯一也不允许其他学校创建相同的类。 我如何能够唯一地识别每个实例 ok Klass 模型,所以它 returns 没有错误。
get() 返回了不止一个 Klass -- 它返回了 2 个!
class ImportStudentsResource(resources.ModelResource):
school = fields.Field(attribute = 'school',column_name='school', widget=ForeignKeyWidget(School, 'name'))
klass = fields.Field(attribute = 'klass',column_name='class', widget=ForeignKeyWidget(Klass, 'name'))
stream = fields.Field(attribute = 'stream',column_name='stream', widget=ForeignKeyWidget(Stream, 'name'))
class Meta:
model = Student
fields = ('school','student_id','name','year','klass','stream')
import_id_fields = ('student_id',)
import_order = ('school','student_id','name','year','klass','stream')
class uploadStudents(LoginRequiredMixin,View):
context = {}
def get(self,request):
form = UploadStudentsForm()
self.context['form'] = form
return render(request,'libman/upload_student.html',self.context)
def post(self, request):
form = UploadStudentsForm(request.POST , request.FILES)
data_set = Dataset()
if form.is_valid():
file = request.FILES['file']
extension = file.name.split(".")[-1].lower()
resource = ImportStudentsResource()
if extension == 'csv':
data = data_set.load(file.read().decode('utf-8'), format=extension)
else:
data = data_set.load(file.read(), format=extension)
result = resource.import_data(data_set, dry_run=True, collect_failed_rows=True, raise_errors=True)
if result.has_validation_errors() or result.has_errors():
messages.success(request,f'Errors experienced during import.')
print("error", result.invalid_rows)
self.context['result'] = result
return redirect('upload_students')
else:
result = resource.import_data(data_set, dry_run=False, raise_errors=False)
self.context['result'] = None
messages.success(request,f'Students uploaded successfully.')
else:
self.context['form'] = UploadStudentsForm()
return render(request, 'libman/upload_student.html', self.context)
class Student(models.Model):
school = models.ForeignKey(School, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
now = datetime.datetime.now()
YEAR = [(str(a), str(a)) for a in range(now.year-2, now.year+2)]
year = models.CharField(max_length=4, choices = YEAR)
student_id = models.CharField(max_length=20,unique=True)
klass = models.ForeignKey(Klass,on_delete=models.CASCADE)
stream = models.ForeignKey(Stream,on_delete=models.CASCADE)
导入数据时,逻辑无法唯一标识 'klass' 的唯一实例。您正在使用与 class 的外键关系的 'name' 属性,但是正如您所说,此 'name' 字段无法唯一标识 Klass 的实例,因此您得到 MultipleObjectsReturned
错误。
解决方案是创建 ForeignKeyWidget
的子class,它使用可以唯一标识 Klass 的覆盖 get_queryset()
。您将需要使用可以添加到查询中的附加参数。我猜 school_id
可能有效(但我不知道你的数据模型)。
class KlassForeignKeyWidget(widgets.ForeignKeyWidget):
"""
Custom widget to lookup a Klass instance
"""
def __init__(
self,
school_id,
field="name",
*args,
**kwargs,
):
super().__init__(self.model, field=field, *args, **kwargs)
self.school_id = school_id
def get_queryset(self, value, row, *args, **kwargs):
return self.model.objects.filter(school_id=self.school_id)
然后您必须在您的资源中声明此自定义小部件:
class ImportStudentsResource(resources.ModelResource):
def __init__(self, school_id):
super().__init__()
self.school_id = school_id
self.fields["klass"] = fields.Field(
attribute="klass",
column_name="class",
widget=myapp.widgets.KlassForeignKeyWidget(
school_id,
),
)
然后当你实例化 ImportStudentsResource