django:get_or_create方法使用注意事项
——1062 - Duplicate entry 'https' for key 'name'

今天遇到一个问题:django一直报错,错误为“1062 - Duplicate entry 'https' for key 'name'”。查询文档后,发现是使用get_or_create方法时出了问题。

问题情境:

有Tag(标签)、Bookmark(书签)、Category(类别)三个model,一个书签有一个若干个标签,其中一个是主标签(作为ForeignKey),每个tag都有一个唯一的Category。代码如下:

class Tag(models.Model):
        name = models.CharField('标签名', max_length = 10, unique = True)
        category = models.ForeignKey('Category')
        pass

class Bookmark(models.Model):

        title = models.CharField('标题', max_length = 100)
        url = models.URLField('路径', max_length = 255, unique = True)
        tags = models.ManyToManyField(Tag, verbose_name = u"标签" )
        primary_tag = models.ForeignKey(Tag, related_name='primarily_used_bookmarks', verbose_name = u"主标签")
        pass
        
class Category(models.Model):
        name = models.CharField('分类名', max_length = 10, unique = True)
        pass

要求是这样的:在前台有一个表单,用来提交书签,该表单可以输入title、url,以及若干个用空格隔开的tags;后台接收到表单后,逐一检查提交的tag,如果某tag在数据库中不存在,则创建之;新创建的的tag时,需要为之指定一个默认的category,并且这个category日后可能被更换。出错的代码如下:

#前置的代码……
cd = form.cleaned_data
defaultCategory = Category.objects.get(name = "默认分类")
tags = map(lambda x : Tag.objects.get_or_create(name = x, category = defaultCategory})[0], cd['tags'])

第一次创建标签(比如“蚁群算法”)时,这段代码不会出错;但是日后如果将该标签的category从“默认分类”改为别的值(比如“人工智能”),并且再次从前台提交该标签时,就会出现“1062 - Duplicate entry 'https' for key 'name'”的错误。

原因分析

get_or_create方法会根据其参数,从数据库中查询符合条件的记录,如果没有符合条件的记录,则会依据参数创建一条新纪录。在前面的情境中,新创建的tag为(name=蚁群算法,category=默认分类);该tag随之被改为(name=蚁群算法,category=人工智能),但是当名为“蚁群算法”的标签再次被提交到后台时,前面的代码还是试图从数据库中查询(name=蚁群算法,category=默认分类)的记录,显然这样的记录已经不复存在,故代码会试图去创建它;但是,由于name是Tag的unique键,而此时数据库中已经存在(name=蚁群算法)的记录了,所以创建失败,报“1062 - Duplicate entry 'https' for key 'name”。

解决方案

get_or_create方法可以有一个“defaults”参数,用来设置指定字段的默认值,前面的代码只需修改如下即可:

cd = form.cleaned_data
defaultCategory = Category.objects.get(name = "默认分类")
tags = map(lambda x : Tag.objects.get_or_create(name = x, category = defaultCategory})[0], cd['tags'])

特别注意

使用get_or_create方法时,如果model有外键,则必须为外键指定一个值,否则会报错“1048, "Column 'XXX_id' cannot be null"”;

例如,前面的代码不能将第三行代码改为:

tags = map(lambda x : Tag.objects.get_or_create(name = x})[0], cd['tags'])

若执意如此,会报“1048, Column 'category_id' cannot be null”。

用支付宝钱包扫描此二维码,为本文付款
本文标签:
django数据库

官方公众号:
查看更多有趣的信息,请扫码关注男儿邦官方微信公众号nanerbangblog。

公众号id:
男儿邦blog

版权声明:
本文为站长原创,如需转载,请联系作者,并以超链接形式注明出处

本文地址:
http://www.nanerbang.com/article/51/