设置唯一的fullname - django - integrityError - 如何解决这个问题?

时间:2014-06-22 10:45:05

标签: python django

我的用户个人资料模型中有一个uniqname字段。逻辑是:如果新用户的名称是john,并且如果db中有其他john,则新john应该得到名称john1。如果在db中有2个johns,则新john应该得到john3

我尝试使用此代码实现此目的:

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,以便我可以在create_account方法中将其设置为uniqname:

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) 

为什么我会收到此错误以及如何实现此目标的任何想法?

2 个答案:

答案 0 :(得分:2)

问题是您的数据库抛出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块。

答案 1 :(得分:0)

在我看来,你可以从'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)

你必须尝试这个..也许有一些错误。我没有测试它。它直接来自我的脑海:)

您应该在模型类中执行此操作。