Django实战-生鲜电商-登录|注册|激活

完成了模型层,需要映射到数据库中,创建相应的表。在项目的 settings.py 文件中配置数据库,Django 有数据读写分离的配置方式。

创建新的项目,Django 会默认使用 sqlite 数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

再执行 python manage.py makemigrations 命令之前,需要在 settings.py 文件中重载新建的 User 模型,然后再执行 python manage.py migrate 命令,将迁移文件,映射到数据库中,创建相应的表。

# django认证系统使用的用户模型
AUTH_USER_MODEL = "users.User"

配置读写分离路由器

DATABASE_ROUTERS = ["utils.db_router.MasterSlaveDBRouter"

在项目路径下创建 utils 文件夹,新建 db_router.py 文件,再创建 MasterSlaveDBRouter 类。

class MasterSlaveDBRouter(object):
    """读写分离路由"""
    def db_for_read(self, model, **hints):
        """读"""
        return "slave"

    def db_for_write(self, model, **hints):
        """写"""
        return "default"

    def allow_relation(self, obj1, obj2, **hints):
        """允许关联查询"""
        return True

一、发送邮件

使用 python 的 celery (分布式任务队列) 模块,实现用户注册邮箱激活功能。

需要在项目路径下,新建 celery_tasks 的 python 包,再创建 tasks.py 文件

from celery import Celery
import os

os.environ["DJANGO_SETTINGS_MODULE"] = "ihome.settings"

# 放到celery服务器上时将注释打开
# import django
# django.setup()

from django.core.mail import send_mail
from django.conf import settings
from goods.models import GoodsCategory, IndexGoodsBanner, IndexPromotionBanner
from goods.models import IndexCategoryGoodsBanner
from django.template import loader

# celery -A celery_tasks.tasks worker -l info


# 创建celery应用对象
app = Celery("celery_tasks.tasks", broker="redis://192.168.108.57/4")


@app.task
def send_active_email(to_email, user_name, token):
    """发送激活邮件"""
    subject = "生鲜用户激活"  # 标题
    body = ""  # 文本邮件体
    sender = settings.EMAIL_FROM  # 发件人
    receiver = [to_email]  # 接收人
    html_body = '<h1>尊敬的用户 %s, 感谢您注册生鲜!</h1>' \
                '<br/><p>请点击此链接激活您的帐号<a href="http://127.0.0.1:8000/users/active/%s">' \
                'http://127.0.0.1:8000/users/active/%s</a></p>' % (user_name, token, token)
    send_mail(subject, body, sender, receiver, html_message=html_body)

在 settings.py 文件中的配置

# Email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_FROM = ''

二、用户注册

class RegisterView(View):
    """用户注册"""
    def get(self, request):
        """处理get请求, 提供注册页面"""
        return render(request, "register.html")

    def post(self, request):
        """处理post请求,处理注册数据"""
        # 获取前端发送的数据/参数
        user_name = request.POST.get("user_name")
        password = request.POST.get("pwd")
        email = request.POST.get("email")
        allow = request.POST.get("allow")

        # 参数校验
        if not all([user_name, password, email]):
            # 如果参数不完整,从定向到注册页面
            return redirect(reverse("users:register"))

        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            # 如果email格式不正确
            return render(request, "register.html", {"errmsg": "邮箱格式不正确!"})

        if allow != "on":
            # 如果用户没有勾选协议
            return render(request, "register.html", {"errmsg": "请同意协议!"})

        # 进行业务逻辑处理
        # 将密码加密
        # 将用户数据保存到数据库中
        # user = User()
        # user.save()

        # 使用django的认证系统创建用户
        try:
            user = User.objects.create_user(user_name, email, password)
        except db.IntegrityError:
            # 如果抛出此异常,表示用户已经注册
            return render(request, "register.html", {"errmsg": "用户已注册!"})

        # 将用户的激活状态设置为假
        user.is_active = False
        user.save()

        # 生成激活token
        token = user.generate_active_token()

        # 使用celery发送邮件
        send_active_email.delay(email, user_name, token)

        # 返回给前端结果
        return redirect(reverse("goods:index"))

三、用户激活

class ActiveView(View):
    """激活"""
    def get(self, request, token):
        # 根据token 解析,获取用户的id
        # 创建转换工具(序列化器)
        s = Serializer(settings.SECRET_KEY, 3600)
        # 解析
        try:
            ret = s.loads(token)
        except SignatureExpired:
            # 如果出现异常,表示token过期,返回信息给用户
            return HttpResponse("激活链接已过期")

        # 更新用户在数据库中的激活状态
        user_id = ret.get("confirm")
        # 查询数据库
        try:
            user = User.objects.get(id=user_id)
        except User.DoesNotExist:
            # 用户不存在
            return HttpResponse("用户不存在")

        user.is_active = True
        user.save()

        # 返回信息给用户
        return redirect(reverse("users:login"))

四、用户登录

class LoginView(View):
    """登录"""
    def get(self, request):
        """提供登录页面"""
        return render(request, "login.html")

    def post(self, request):
        """处理登录请求"""
        user_name = request.POST.get("username")
        password = request.POST.get("pwd")
        remembered = request.POST.get("remembered")

        if not all([user_name, password]):
            return redirect(reverse("users:login"))

        #
        # password = sha256(password)
        # User.objects.get(username=user_name, password=password)

        # 使用django的认证系统
        user = authenticate(username=user_name, password=password)
        if user is None:
            # 表示用户的认证失败
            return render(request, "login.html", {"errmsg": "用户名或密码错误"})

        # 表示用户认证成功
        # 判断用户的激活状态
        if user.is_active is False:
            # 表示用户未激活
            return render(request, "login.html", {"errmsg": "用户名尚未激活"})

        # 在session中保存用户的登录状态信息
        login(request, user)

        # 处理记住用户名的逻辑
        if remembered != "on":
            # 不需要记住用户状态
            # 使用set_expiry设置 session 有效期
            request.session.set_expiry(0)
        else:
            # 需要记住用户状态
            request.session.set_expiry(None)

        # 将cookie中的购物车数据与redis中的购物车数据合并
        # 从cookie中获取购物车数据
        cart_json = request.COOKIES.get("cart")
        if cart_json is not None:
            cart_cookie = json.loads(cart_json)
        else:
            cart_cookie = {}

        # 从redis中获取购物车数据
        redis_conn = get_redis_connection("default")
        cart_redis = redis_conn.hgetall("cart_%s" % user.id)

        # 进行合并
        # cart_redis.update(cart_cookie)
        for sku_id, count in cart_cookie.items():
            # 在redis中的键与值都是bytes类型, 在cookie中的sku_id是str类型
            sku_id = sku_id.encode()  # 将str类型的sku_id转为bytes类型
            if sku_id in cart_redis:  # {b'1': b'3'}
                # cookie中有的商品,在redis中也有,进行数量求和,再设置到redis对应的购物车中
                origin_count = cart_redis[sku_id]
                count += int(origin_count)

            cart_redis[sku_id] = count

        # 将合并的购物车数据保存到redis中
        if cart_redis:
            redis_conn.hmset("cart_%s" % user.id, cart_redis)

        # 清除cookie中的购物车数据
        # 登录成功,根据next参数跳转页面
        next = request.GET.get("next")
        if next is None:
            # 如果没有next参数,跳转到主页
            response = redirect(reverse("goods:index"))
        else:
            # 如果next存在,跳转到next路径
            response = redirect(next)

        response.delete_cookie("cart")
        return response

原文发布于微信公众号 - 数据云团(SmartData)

原文发表时间:2019-05-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券