关于django事务

关于django事务

#@transaction.atomic
def do_register(self, request):

按照视频的方法,如果使用这个自动提交事务的话,那因为下面用了try/except

try:
    # 对用户表插入一条数据
    user = User.objects.create_user(
        username= username,
        password= password,
        nickname= nickname
    )
except Exception as e:
    print(e)
    return None

会导致这个自动提交事务,起不到作用。添加数据的时候如果出错,前面正确的添加还是会起效果。就不能达到必须所有数据都正确才新增数据。基于这点,我改成了手动提交事务。

    但是如果手动提交事务的话,写在注册里面的登录就起不到作用,我的理解是手动开启事务的话,需要手动提交,那在所有的表验证完需要插入的数据后,会一起写入数据。那写在注册里面的登录功能,这时应该还找不到可以登录的对象,应该就无法登录吧。我不知道我的理解有没有问题,想请老师看一下我的想法。

这是我想问的第一个问题?

    就是如果使用了手动提交事务,那是不是就不能把登录和注册写在一个方法里面了,因为这样的话,在运行到登录这个代码的时候,其实事务还没有提交,那数据库也不存在这条数据,就找不到这个对象,更无法登录。

然后,基于它无法登录。但我又想实现注册后登录的功能。我又在表单中新增了一个方法。

def register_to_login(self, request, user):
    # 执行登录
    transaction.set_autocommit(True)
    login(request, user)
    user.last_login = now()
    user.save()
    # 对登录记录表插入一条数据

    ip = request.META.get('REMOTE_ADDR', None)

    user.add_login_record(
        username=user.username,
        ip=ip,
    )

我测试多次发现,在这个方法中,必须要把自动开启事务重新变成true才能够登录成功,否则调用也不会执行。

这里我想问老师第二个问题?

    就是我是在上一个方法中关闭了自动提交事务,从而去手动提交,那为什么还会影响到另一个方法?(我的调用是在视图里面通过验证且先执行了do_register方法后,再调用的这个方法)

def form_valid(self, form):
    result = form.do_register(request= self.request)
    if result is not None:
        user, profile = result
        data = {
            'user': UserSerializer(user).to_dict(),
            'profile': UserProfileSerializer(profile).to_dict()
        }
        form.register_to_login(request=self.request, user=user)
        return JsonResponse(data)
    else:
        return ServerErrorJsonResponse()

那关闭了自动提交事务的话,它影响的范围是什么样的?是在这个类中再定义任何方法,都会受到它的影响?那如果我在注册的方法中关闭了自动提交事务,会不会影响其他表单关于事务的使用?

 


正在回答

登陆购买课程后可参与讨论,去登陆

1回答

同学,你好!

1、transaction.atomic装饰器可以保证该函数中所有的数据库操作都在一个事务中,是要整体正确执行才可以正常插入数据,同学理解的是正确的,要先注册成功才可以登录成功

2、使用transaction.set_autocommit()时需要传入autocommit的值,True是自动提交,False是不自动提交

若放弃自动提交(transaction.set_autocommit(False))的话是需要手动提交的,执行transaction.commit()手动提交事务

例:手动提交需要在提交的代码后写代码提交事务才可以

# 放弃自动提交
transaction.set_autocommit(False)
user = User.objects.create_user(
    username=data.get('username', None),
    password=data.get('password', None),
    nickname=data.get('nickname', None)
)
# 手动提交事务
transaction.commit()
# 2. 创建详细表
profile = Profile.objects.create(
    user=user,
    username=user.username,
    version=version,
    source=source
)
# 手动提交事务
transaction.commit()
# 3. 执行登录
login(request, user)
# 4. 记录登录日志
user.last_login = now()
user.save()
ip = request.META.get('REMOTE_ADDR', '')
user.add_login_record(username=user.username, ip=ip, source=source, version=version)
# 手动提交事务
transaction.commit()
return user, profile

3、在do_register()方法中设置手动提交并且事务正确提交的话是不会影响register_to_login()方法的,只会影响当前函数中的代码

祝学习愉快!

  • AShySherry 提问者 #1
    def do_register(self, request):
    
        username = self.cleaned_data.get('username', None)
        password = self.cleaned_data.get('password', None)
        nickname = self.cleaned_data.get('nickname', None)
        source = request.headers.get('source', None)
        version = request.headers.get('version', None)
    
        transaction.set_autocommit(False)
    
        try:
            # 对用户表插入一条数据
            user = User.objects.create_user(
                username= username,
                password= password,
                nickname= nickname
            )
    
            # 对用户详情表插入一条数据
            profile = Profile.objects.create(
                username= username,
                source= source,
                version= version,
                user=user
            )
    
            transaction.commit()
            return user, profile
        except Exception as e:
            print(e)
            transaction.rollback()
            return None
    def register_to_login(self, request, user):
        # 执行登录
        transaction.set_autocommit(True)
        login(request, user)
        user.last_login = now()
        user.save()
        # 对登录记录表插入一条数据
    
        ip = request.META.get('REMOTE_ADDR', None)
    
        user.add_login_record(
            username=user.username,
            ip=ip,
        )

    老师,那为什么我这边register_to_login方法,如果不加transaction.set_autocommit(True)就一直执行不了登录呢? 因为我尝试了很多次,如果不加这句去开启自动提交事务的话,同样去调用这个函数,但是它就不会执行里面的内容。但是这个函数本身是没有加装饰器去开启事务的,只是在do_register函数中用到了事务,并没有在这里用到,所以我才会想是不是因为上一个函数影响到了这个函数。

    有没有可能是因为这里的user是通过do_register函数的返回值获取的,所以这里需要再开启一下自动提交呢?

    2022-05-17 20:14:20
  • 好帮手慕美 回复 提问者 AShySherry #2

    同学,你好!若do_register()可以正常执行,数据插入成功,是会执行register_to_login()中的代码的

    下图是执行同学提供的代码插入的数据:

    https://img1.sycdn.imooc.com//climg/62844eea0986055d14570332.jpg

    同学可以重新启动下服务后再试下,若还是有问题,同学可以将项目发送至1757578369@qq.com邮箱,老师这边运行一下代码

    祝学习愉快!

    2022-05-18 09:43:51
问题已解决,确定采纳
还有疑问,暂不采纳

恭喜解决一个难题,获得1积分~

来为老师/同学的回答评分吧

0 星
请稍等 ...
意见反馈 帮助中心 APP下载
官方微信

在线咨询

领取优惠

免费试听

领取大纲

扫描二维码,添加
你的专属老师