我的系统有产品,图像与之关联,如下所示:
class Product(models.Model):
name = models.CharField(max_length=100)
...
class Image(models.Model):
product = models.ForeignKey(Product)
image = models.ImageField(upload_to='products')
到目前为止一切顺利。当然,客户希望在csv中批量上传他们的产品并上传包含图像的zip文件。我将csv格式化为:
product_name,image_1.jpg,image_2.jpg,...
product_2,image.jpg,...
到目前为止,我已经制作了一个模型作为帮助者:
class BulkUpload(models.Model):
csv = models.FileField(upload_to='tmp')
img_zip = models.FileField(upload_to='tmp')
工作流程如下:
如果我们在任何时候回滚,我们应该以某种方式告知用户出了什么问题。
我最初的想法是覆盖保存或使用post_save信号,但无法访问请求意味着我既不能使用消息也不会引发验证错误。在管理员中覆盖model_save()会遇到问题,无法进行任何验证。
所以现在我的想法是改变ModelForm并将其交给django管理员。我可以覆盖clean()方法,引发ValidationErrors并(可能)在事务中运行我的所有东西。但我正在努力弄清楚如何以这样的方式访问文件,以便我可以在它们上使用Python的ZipFile和csv库。在表单验证方法中进行实际工作也感觉有点脏,但我不知道我还能在哪里做到。
我可能已经详细介绍了,但我想解释一下解决方案,以便提出替代解决方案。
答案 0 :(得分:2)
我认为您不应该使用BulkUpload
或任何代表此操作的模型,至少如果您打算同时执行同步过程提示。我会在管理区域添加一个额外的视图,by hand或使用third party library,然后我会处理该表单并执行工作流程。
但无论如何,鉴于您已经拥有BulkUpload
模型,使用admin.ModelAdmin
对象肯定会更容易。您的主要关注点似乎是您应该放置交易代码的位置。正如您所提到的,有几种选择。在我看来,最好的选择是将流程分为两部分:
首先,在您的模型的clean
方法中,您应该检查用户可能产生的所有潜在错误:已存在的图像,缺失的图像,重复的产品等等。在这里你应该检查上传的文件是否正常,例如使用:
def clean(self):
if not zipfile.is_zipfile(self.img_zip.file):
raise ValidationError('Not a zip file')
之后,您知道此点可能产生的任何错误都将由系统错误产生:bd失败,HD没有足够的空间等等,因为所有其他可能的错误都应该在前一步。在ModelAdmin.save_model
方法中,您应该执行工作流程的其余部分。您可以使用ModelAdmin.message_user
通知用户任何错误。
对于上传文件的实际处理,您将其命名为:只需使用标准库中的zipfile和csv模块即可。您应该创建一个ZipFile对象并将其解压缩到某处。现在,您应该使用csv.reader查看csv文件的数据。这样的事情(未经测试):
def save_model(self, request, obj, form, change):
# ...
with open('tmp/' + obj.img_zip.name, 'r') as csvfile:
productreader = csv.reader(csvfile)
for product_details in productreader:
p = Product(name=product_details[0])
p.save()
for image in product_details[1:]:
i = ImageField()
i.product = p
i.image = File(open('tmp/' + image)) # not tested
i.save()
毕竟,拥有BulkUpload
实例是没有意义的,所以你应该删除它。这就是为什么我在开始时说那个模型有点无用。
显然,你需要为交易和其他一些东西添加代码,但我希望你能得到一般的想法。