Django加载对象以防唯一键是Duplicate

时间:2013-05-26 09:10:50

标签: python django django-models

我有以下型号:

class Car(models.Model):
 id = models.AutoField(primary_key=True)
 slug = models.CharField(max_length=128L, unique=True, blank=True)
 name = models.CharField(max_length=128L, blank=True)
 model_slug = models.CharField(max_length=128L, unique=True, blank=True)
 ...

每当我从XML文件中获取Car列表时,其中一些会显示多次 (使用相同的slug),如果我试图保存,我会得到IntegrityError(1062, "Duplicate entry 'car-slug' for key 'slug'")

我希望它加载现有的Car(或更新它,以更容易的方式),以防出现重复错误。
但我希望它足够通用,以便它适用于model_slug(或任何产生该错误的唯一字段)。

3 个答案:

答案 0 :(得分:1)

您需要get_or_create

car,created = Car.objects.get_or_create(slug='car-slug')
if created:
   print 'New car was created'
   car.slug = 'new-car-slug'
else:
   # do whatever needs to be done here
   # with the existing car object, which will
   # be car
   # car.name = 'new name'

car.save()

无论您向get_or_create提供什么参数,它都会使用这些参数来搜索模型的现有记录。


假设您不知道哪个字段组合会触发重复。简单的方法是找出模型中哪些字段具有该限制(即unique=True)。您可以从模型中反省这些信息,或者更简单的方法是将这些字段简单地传递给get_or_create

第一步是在XML字段和模型的字段之间创建映射:

xml_lookup = {}
xml_lookup = {'CAR-SLUG': 'slug'}  # etc. etc.

如果需要,您可以填充所有字段,但我的建议是仅使用那些对它们有唯一约束的字段填充它。

接下来,在解析XML时,为每条记录填充字典,映射每个字段:

for row in xml_file:
    lookup_dict = {}
    lookup_dict[xml_lookup[row['tag']] = row['value']  # Or something similar
    car, created = Car.objects.get_or_create(**lookup_dict)
    if created:
       # Nothing matched, a new record was created
       # Any any logic you need here
    else:
       # Existing record matched the search parameters
       # Change/update whatever field to prevent the IntegrityError
       car.model_slug = row['MODEL_SLUG']
       # Set/update fields as required
    car.save()  # Save the modified object

答案 1 :(得分:0)

您可以先在内存中过滤重复的汽车条目,然后在新项目上调用create,例如,

uniq_attrs = ['slug', 'model_slug', ...]
existed_attrs = set()
for car in car_list:
    # skip the duplicates
    if any([(attr, car[attr]) in existed_attrs for attr in uniq_attrs):
        continue
    uniq_attrs.update([(attr, car[attr]) for attr in uniq_attrs])

    # save the new car to db
    Car.objects.save(....)

或者您可以在唯一字段上尝试get_or_create,然后使用模型保存保存其他属性,例如,

for car in car_list:
    attr_dict = {attr:car[attr] for attr in uniq_attrs}
    car, created = Car.objects.get_or_create(**attr_dict)

    # already created before
    if created:
        continue

    # save other attributes
    car.set(...)
    car.set(...)
    car.save()

答案 2 :(得分:0)

我最终在模型上编写了一个自定义保存(这样我的代码不会改变,只是模型):

def save(self, *args, **kwargs):
    try:
        super(Car, self).save(*args, **kwargs)
    except IntegrityError, e:
        existing_object = Car.objects.get(slug=self.slug)
        self = existing_object
        super(Car, self).save(*args, **kwargs)
        return existing_object.id

现在我返回对象的ID来分配它,所以save()命令如下所示:

the_id = generic_object.save() #e.g. Car
if the_id:
    generic_object.id = the_id