Python入门自学进阶-Web框架——30、DjangoAdmin项目应用-自定义用户认证续

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

一、前面实现的是DjangoAdmin实现的自定义用户认证管理现在自己来实现管理功能即在mytestapp中增加用户认证管理功能。

在UserProfile的model中对password字段增加help_text属性

password = models.CharField(_('password'), max_length=128,help_text=mark_safe('<a href="password/">重置密码</a>.'))

然后在用户修改页面进行显示    {{ f }}{{ f.help_text }}<span>{{ f.errors }}</span>

然后点击这个重置密码链接跳转到一个密码修改页面。

在urls中增加对应的路由项

path('<str:app_name>/<str:table_name>/<int:id_num>/change/password/',views.passwd_reset,name='passwd_reset'),

编写视图函数passwd_reset

def passwd_reset(req,app_name,table_name,id_num):
    admin_class = mytestapp_admin.enable_admins[app_name][table_name]
    model_form_class = myutils.create_model_form(req, admin_class)
    obj = admin_class.model.objects.get(id=id_num)
    errors ={}
    if req.method == "POST":
        _password = req.POST.get("password")
        _password2 = req.POST.get("password2")
        if _password == _password2:
            obj.set_password(_password)   # 借助模型中的函数
            obj.save()
            return redirect(req.path.rstrip("password/"))
        else:  # 两次密码输入不相同报错
            errors['invalid_password'] = "two password not same"
    return render(req,'mytestapp/passwd_reset.html',{'user_obj':obj,'errors':errors})

 前端页面passwd_reset.html

{% extends 'base.html' %}
{% load tags %}
{% block mybody %}
    <body>
        <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
            <a class="navbar-brand col-md-3 col-lg-2 mr-0 px-3" href="#">我的客户管理系统</a>
            <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-toggle="collapse" data-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <ul class="navbar-nav px-3">
                <li class="nav-item text-nowrap">
                    <a class="nav-link" href="#">{{ request.user.userprofile.name }}</a>
                </li>
            </ul>
        </nav>
        <div class="container-fluid" style="margin-top: 20px;margin-left: 50px;">
            <div class="d-flex p-2 bd-highlight">修改用户【{{ user_obj }}】密码</div>
            <div class="d-inline-flex p-2 bd-highlight">
                <form method="post">{% csrf_token %}
                      <div class="form-group">
                        <label for="exampleInputEmail1">新密码</label>
                        <input type="password" class="form-control" name="password">
                      </div>
                      <div class="form-group">
                        <label for="exampleInputPassword1">重复密码</label>
                        <input type="password" class="form-control" name="password2">
                      </div>
                    <div>
                        <ul>
                            {% for k,v in errors.items %}
                            <li>{{ k }}:{{ v }}</li>
                            {% endfor %}
                        </ul>
                    </div>
                      <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </div>
        </div>
    </body>
{% endblock %}

二、在ModelForm中排除部分字段

在modelform形成过程中在Meta中使用exclude进行排除。

class Meta:
    model = admin_class.model
    fields = "__all__"
    exclude = ('qq',)

这样配置后在前端就不会形成qq这个字段了。使用exclude排除主要用在增加新记录时如果某些字段值是数据库插入时自动生成的如创建日期等不需要在前端显示添加或修改时有些字段设置为readonly在前端使用disabled进行了输入禁止但是在提交时这个字段不提交就形成null值与原值不相等导致校验失败这时可以直接使用exclude排除这些字段不显示就可以了相当于update语句不修改这些字段。

做成配置的在AdminClass中增加exclude_fields=['qq',]

在生成ModelForm模板中

class Meta:
    model = admin_class.model
    fields = "__all__"
    if admin_class.exclude_fields:
        exclude = admin_class.exclude_fields

三、在显示记录列表中是按照AdminClass中的list_display中的字段显示的这些字段都是在数据库表中存在的如果我们要添加一个数据库表中没有的字段如何实现如增加一个enrollment字段内容显示报名并是一个链接点击后跳转到其他页面。

class CustomerAdmin(BaseAdmin):
    list_display = ['qq','name','phone','source','consultant','referral_from','consult_course','tags','status','enroll']
    list_per_page = 4
    list_filter = ['qq','source','status','consult_course','tags']
    list_search = ['qq','name']
    filter_horizontal = ['tags']
    readonly_fields = ['qq','consultant','tags']
    actions = ['delete_action',]

    def enroll(self):
        return "what"   # enroll字段显示的值
....

添加如上配置后显示客户表时会报错

使用try捕获错误进行处理

@register.simple_tag
def build_table_row(obj,admin_class,url_path):
    row_ele = ""
    for row_data in obj:
        row_ele = row_ele +'<tr><td colspan="6"><input  my_id="obj_checkbox" type="checkbox" value="%s"</td>'%(row_data.id)
        for index_ele,column in enumerate(admin_class.list_display):
            try:
                field_obj = row_data._meta.get_field(column)
                if field_obj.choices:
                    column_data = getattr(row_data,"get_%s_display"%column)()
                else:
                    column_data = getattr(row_data,column)
                field_obj1 = getattr(row_data,column)
                if hasattr(field_obj1,'values'):
                    s = ""
                    dic1 = field_obj1.values()
                    for i in range(dic1.count()):
                        for v in dic1[i].values():
                            s = s + str(v) + ';'
                        column_data = s
                if index_ele == 0:   # 若果是第一列则加上a标签,可以跳转到修改页
                    row_ele += '<td colspan="6"><a href="%s%s/change/">%s</a></td>'%(url_path,row_data.id,column_data)
                else:
                    row_ele += "<td colspan='6'>%s</td>"%column_data
            except FieldDoesNotExist as e:
                if hasattr(admin_class,column):
                    column_func = getattr(admin_class,column)
                    admin_class.instance = obj
                    # 将obj传递给admin_class的instance这样就能取到对应行的对象的id这里obj是QuerySet对象

                    column_data = column_func()
                    row_ele += "<td colspan='6'>%s</td>" % column_data
        row_ele = row_ele + "</tr>"
    print(row_ele)
    return mark_safe(row_ele)

在admin_class中配置

    def enroll(self):

        return "<a href='%s/enrollment/'>报名</a>"%self.instance[0].id
        # 因为instance是QuerySet对象对于每一行只有一个对象取[0]这样形成的连接就是id/enrollment/,在路由项中增加对应的路由跳转到对应view函数处理

点击报名

 

增加对应的路由项和视图及前端模板完成报名的功能。 

 四、自定义用户登录验证

前面使用的是DjangoAdmin的登录界面

 登陆后进入的是各种表的管理界面现在要自定义登录界面登陆后进入业务界面。

自己做一个登录界面

 

{% extends 'base.html' %}
{% block mybody %}
    <div class="container" >
        <form class="form-signin col-sm-3">

          <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
          <label for="inputEmail" class="sr-only">Email address</label>
          <input name="email" type="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
          <label for="inputPassword" class="sr-only">Password</label>
          <input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required>
          <div class="checkbox mb-3">
            <label>
              <input type="checkbox" value="remember-me"> Remember me
            </label>
          </div>
          <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>

        </form>
    </div>
{% endblock %}

增加路由项path('account/login/',views.acc_login),

编写视图函数acc_login基本框架

def acc_login(req):

    return render(req,'login.html')

此时访问http://127.0.0.1:8000/account/login/就可以访问到上面的自定义登录界面

修改完善

前端

{% extends 'base.html' %}
{% block mybody %}
    <div class="container" >
        <form class="form-signin col-sm-3" method="post">{% csrf_token %}

          <h1 class="h3 mb-3 font-weight-normal">PlswCRM</h1>
          <label for="inputEmail" class="sr-only">Email address</label>
          <input name="email" type="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
          <label for="inputPassword" class="sr-only">Password</label>
          <input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required>
          {% if errors %}
            <span style="color: red">{{ errors.error }}</span>
          {% endif %}
          <div class="checkbox mb-3">
            <label>
              <input type="checkbox" value="remember-me"> Remember me
            </label>
          </div>
          <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>

        </form>
    </div>
{% endblock %}

后端视图函数

from django.shortcuts import render,redirect
from django.contrib.auth import authenticate,login
# 这里的authenticate和login是借助于django的认证和登录功能

def acc_login(req):

    errors={}
    if req.method == "POST":
        _email = req.POST.get('email')
        _password = req.POST.get('password')
        user = authenticate(username=_email,password=_password)
        print(user)
        # authenticate接受用户名和密码如果验证通过则user为用户对象否则为空
        if user:
            # 如果用户存在即验证通过需要将用户写入session中做保存再次访问网站其他页面时使用
            # 这个写session的动作可以使用login()来实现
            login(req,user)  # 利用Django的login实现用户写入session即保存登录
            # 验证、登录成功转到首页
            return redirect("/plcrm/")
        else:
            errors['error'] = "username or password is wrong"
    return render(req,'login.html',{"errors":errors,})

对需要登录验证的视图函数增加验证

如前面的mytestapp的各个视图函数访问前需要进行实现登录验证这时还是借助Django的装饰器来实现

from django.contrib.auth.decorators import login_required

# Create your views here.
@login_required   # 加上这个装饰器就会在访问index函数时进行验证如果没有登陆则跳转到登录页面
def index(req):
    print(mytestapp_admin.enable_admins['plcrm']['customer'].model)
    return render(req,"mytestapp/index.html",{'table_list':mytestapp_admin.enable_admins})

在没有登陆的状态下访问mytestapp

自动跳转到accounts/login/路径这个是Django默认的登录路径而我们自定义的是account/login/这个选项在settings.py中进行配置

 LOGIN_URL = '/account/login/'

这时就会自动跳转到自定义的/account/login/这个登录界面。

将其他的视图函数都加上@login_required这样就实现了所有页面都需要登录认证的功能。

五、登录退出

在用户名称上增加下列框

实现登录退出<a class="dropdown-item" href="{% url 'acc_logout' %}">login out</a>

在路由项中增加path('account/logout/',views.acc_logout,name='acc_logout'),

视图函数

def acc_logout(req):
    logout(req)
    return redirect('/account/login/')

logout也是借助如django的功能。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: gopython