2014-06-22 28 views
0

我在我的用户配置文件模型中有一个uniqname字段。逻辑是:如果新用户的名称是john,并且db中有其他约翰,则新约翰应该获得名称john1。如果2个johns在db中,新的john应该得到john3设置唯一全名 - django - integrityError - 如何解决这个问题?

我试图用这个代码来实现这一点:

def set_uniqname(userprofile, fullname, i=0): 
    new_uniqname = u"{}{}".format(fullname, str(i) if i else '') 
    try: 
     userprofile.uniqname = new_uniqname 
     userprofile.save() 
     return userprofile 
    except IntegrityError: 
     i += 1 
     return set_uniqname(userprofile, fullname, i) 

name = request.POST.get('name').title() 
email = request.POST.get('email') 
passwort = request.POST.get('passwort') 

""" create base django user with random username """ 
random_username = ''.join(random.sample(string.ascii_lowercase, 26)) 
new_user = User.objects.create_user(random_username, email, passwort) 

""" update the created userprofile with unique fullname for URL """ 
userprofile = new_user.get_profile() 


uniqname = name.replace(' ','') 
userprofile = set_uniqname(userprofile, uniqname) 

但我得到的错误:

current transaction is aborted, commands ignored until end of transaction block 

在用户配置文件中的uniqname场这样的定义:

uniqname = models.TextField(unique=True, default=None, null=True) 

我在post_save信号中将此字段设置为None,以便我可以将其设置为uniqname在我的create_account方法中:

def create_profile(sender, **kw): 
    user = kw["instance"] 
    if kw["created"]: 
     up = Person(user=user) 
     up.uniqname = None 
     up.save()   
post_save.connect(create_profile, sender=User) 

任何想法,为什么我得到这个错误,我如何实现这个?

回答

1

问题是您的数据库抛出了一个IntegrityError,并且您不处理数据库级别的异常。您必须将当前事务回滚到没有错误的点。由于您可能不想回滚到封装整个视图的事务的开始,因此您需要在atomic块中运行代码。然后,你就可以回滚眼前这个特定的语句:

from django.db import transaction 

def set_uniqname(userprofile, fullname, i=0): 
    new_uniqname = u"{}{}".format(fullname, str(i) if i else '') 
    try: 
     with transaction.atomic(): 
      userprofile.uniqname = new_uniqname 
      userprofile.save() 
      return userprofile 
    except IntegrityError: 
     i += 1 
     return set_uniqname(userprofile, fullname, i) 

atomic()将在块的开始自动创建一个保存点,而当块是成功的,它提交到数据库。如果发生错误,块将回滚到保存点,并允许错误向上传播到try块。

+0

我正在使用django 1.4 :(''atomic''只是从1.6开始,对吧? – doniyor

+0

@ doniyor你是对的,在1.4中你可以使用'transaction.commit_on_success()'。 – knbk

+0

就像一个魅力!谢啦 – doniyor

-1

在我看来,你可以覆盖'YourModel'的save()方法。这是我的想法的例子。

def _get_unique_name(self, name, count=0): 
    """ 
    Method returns unique name. 
    """ 
    try: 
     name = YourModel.objects.get(name=name) 
     count = count + 1 
     name = "%s%s" % (name, count) 
     self._get_unique_name(name, count) #recursion 
    except: 
     return name 


def save(self, *args, **kwargs): 
    """ 
    Overides save method from YourModel. 
    """ 
    self.name = self._get_unique_name(self.name) 
    super(YourModel, self).save(*args, **kwargs) 

你必须尝试这个..也许有一些错误。我没有测试它。这是直接从我的头:)

你应该在你的模型类中做到这一点。