Python CRM项目八

自定义用户认证

目的:实现Django自定义的认证系统,在生产环境都是根据此代码进行定制的

步骤:

  1.在settings文件中配置要使用的类

                 #命名规则 app名称.类名      
AUTH_USER_MODEL = 'crm.UserProfile'

  2.在crm app下的models文件中加入Django官方的用户认证

from django.contrib.auth.models import (
    BaseUserManager, AbstractBaseUser,PermissionsMixin
)
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
class UserProfileManager(BaseUserManager): def create_user(self, email, name, password=None):     #创建用户根据UserProfile中的字段,输入 if not email: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email), name=name, ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, name, password):     #创建超级用户根据UserProfile中的字段,输入 user = self.create_user( email, password=password, name=name, ) user.is_admin = True user.save(using=self._db) return user class UserProfile(AbstractBaseUser,PermissionsMixin):
#使用Django自带的登录系统,可以自定义一些字段,例如邮箱,密码,用户名 email
= models.EmailField( verbose_name='email address', max_length=255, unique=True, ) password = models.CharField(_('password'), max_length=128,help_text = mark_safe('<a href="password/">修改密码</a>')) name = models.CharField(max_length=32) is_active = models.BooleanField(default=True) is_admin = models.BooleanField(default=False)   
  #在创建用户的时候调用该方法进行用户的创建 objects
= UserProfileManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['name'] def get_full_name(self): # The user is identified by their email address return self.email def get_short_name(self): # The user is identified by their email address return self.email def __str__(self): # __unicode__ on Python 2 return self.email def has_perm(self, perm, obj=None): "Does the user have a specific permission?" # Simplest possible answer: Yes, always return True def has_module_perms(self, app_label): "Does the user have permissions to view the app `app_label`?" # Simplest possible answer: Yes, always return True @property def is_staff(self): "Is the user a member of staff?" # Simplest possible answer: active users are staff return self.is_active class Meta: verbose_name_plural = '用户'

  3.在crm app下的admin中

from django import forms
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from crm.models import UserProfile

class UserCreationForm(forms.ModelForm):
  #在Django Admin页面中用户创建的表单展示
"""A form for creating new users. Includes all the required fields, plus a repeated password.""" password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) class Meta:
#展示的model对象和字段 model
= UserProfile fields = ('email', 'name') def clean_password2(self):
#判断两次输入的密码是否一致
# Check that the two password entries match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True):
     #保存用户到数据库
# Save the provided password in hashed format user = super(UserCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class UserChangeForm(forms.ModelForm):
#在Django Admin页面中用户修改的表单展示
"""A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. """ password = ReadOnlyPasswordHashField() class Meta:
     #展示的model对象和字段   model
= UserProfile fields = ('email', 'password', 'name', 'is_active', 'is_admin') def clean_password(self): # Regardless of what the user provides, return the initial value. # This is done here, rather than on the field, because the # field does not have access to the initial value return self.initial["password"] class UserProfileAdmin(BaseUserAdmin):
#在在Django Admin页面中配置的admin_class
# The forms to add and change user instances form = UserChangeForm #调用修改用户的表单 add_form = UserCreationForm #调用创建用户的表单 # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ('email', 'name', 'is_admin') list_filter = ('is_admin',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('name',)}), ('Permissions', {'fields': ('is_admin','is_active','user_permissions',)}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'name', 'password1', 'password2')} ), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = ('groups','user_permissions') admin.site.unregister(Group)
#把models对象中的model对象和admin_class对象组合起来 admin.site.register(models.UserProfile,UserProfileAdmin)

  4.在king_admin中实现修改密码的功能

 在king_admin的urls.py中配置

url(r'^(w+)/(w+)/(d+)/change/password/$',views.password_reset,name='password_reset'),

 在king_admin中的king_admin.py中配置

class UserAdmin(BaseAdmin):
    list_display = ['email','name']   #首页展示的字段
    readonly_fields = ['password',]   #只读字段 
    modelfrom_exclude_fields = ['last_login','is_superuser','groups','user_permissions']   #不展示的字段

 在views函数中开发该模块

def password_reset(request,app_name,table_name,obj_id):
    '''动态修改密码'''
#获取admin_class类和要修改密码的对象
admin_class = king_admin.enabled_admins[app_name][table_name] model_obj = admin_class.model.objects.get(id=obj_id) errors = {} if request.method == 'POST':
#获取前端页面的两个值,密码和新密码 _password1
= request.POST.get('password1') _password2 = request.POST.get('password2')
#如果两次密码相同,并且长度大于5位,则调用父类的方法保存密码,同时入库,最后返回到展示页面
if _password1 == _password2: if len(_password2) > 5: model_obj.set_password(_password1) model_obj.save()
#保存成功则跳转到展示页面进行展示
return redirect(request.path.rstrip('password/')) else: errors['invalid_password'] = '密码长度不足6位' else: errors['invalid_password'] = '两次密码不一致' return render(request,'king_admin/password_reset.html',{'model_obj':model_obj})

  5.在forms.py中将不需要展示的字段写到exclude上

class Meta:
    model = admin_class.model
    fields = '__all__'
    exclude = admin_class.modelfrom_exclude_fields     #排除的字段

  6.前端页面

  本质上是一个form表达,展示用户的用户名,然后用户填写密码和新密码之后提交到views的方法中进行修改密码的操作

{% extends 'king_admin/table_index.html' %}

{% block container %}
    <div class="row">
        <div class="panel panel-info">
            <div class="panel-heading">
                <h3 class="panel-title">重置用户{{ model_obj.name }}密码</h3>
            </div>
            <div class="panel-body">
                <form method="post" class="form-horizontal">
                    {% csrf_token %}
                    <div class="form-group">
                        <label class="col-sm-1" style="font-weight:normal">
                            用户名:
                        </label>
                        <div class="col-sm-3">
                            <input class="form-control" type="text" value="{{ model_obj.email }}" disabled>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-1" style="font-weight:normal">
                            密码:
                        </label>
                        <div class="col-sm-3">
                            <input class="form-control" type="password" name="password1">
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-1" style="font-weight:normal">
                            重复密码:
                        </label>
                        <div class="col-sm-3">
                            <input class="form-control" type="password" name="password2">
                        </div>
                    </div>
                    <div>
                        <ul style="color:red">
                            {% for k,v in errors.items %}
                                <li>{{ k }}-{{ v }}</li>
                            {% endfor %}
                        </ul>
                    </div>
                    <input type="submit" class="btn btn-info" style="margin-left:110px" value="提交">
                    <input type="reset" class="btn btn-danger" style="margin-left:30px" value="重置">
                </form>
            </div>
        </div>
    </div>
{% endblock %}

 自定义用户登录

  目的:利用Django提供的组件,实现自己的用户认证系统,包括登录,登出和利用装饰器实现方法的登录校验

  1.在settings文件中配置登录url的路径

LOGIN_URL = '/'

  2.在入口的app中配置url

  3.在views中开发登录,登出和首页的模块

from django.shortcuts import render,redirect
from django.contrib.auth import login,authenticate,logout
# Create your views here.

def account_login(request):
    errors = {}
    if request.method == 'POST':
#获取前端表单的值 _email
= request.POST.get('email') _password = request.POST.get('password') #使用Django自带的用户认证 user = authenticate(username=_email,password=_password) if user:
#登录成功则进行跳转,如果有next_url则跳转到下一个页面,否则跳转到首页 login(request,user) next_url
= request.GET.get('next','') if next_url: return redirect(next_url) else: return redirect('/index/') else: errors['error'] = '用户名密码不正确' return render(request,'login.html',{'errors':errors}) def account_logout(request):
#用户登出 logout(request)
return redirect('/account/login/') def index(request): return render(request,'index.html')

  4.在需要登录校验的方法上,加上@login_required装饰器

from django.contrib.auth.decorators import login_required

@login_required
def index(request):
    return render(request, 'king_admin/table_index.html',{'table_list':king_admin.enabled_admins})

  5.前端页面,form表单以post的方式向后台发送用户名和密码,后端的views中相应的方法进行校验

  {% extends 'base.html' %}

  {% block body %}
      <div class="row">
        <div class="panel panel-info">
            <div class="panel-heading">
                <h3 class="panel-title">登录CRM系统</h3>
            </div>
            <div class="panel-body ">
              <form class="form-horizontal" method="post">{% csrf_token %}
                <div class="form-group">
                        <label class="col-sm-1" style="font-weight:normal">
                           邮箱:
                        </label>
                        <div class="col-sm-3">
                            <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
                        </div>
                </div>
                <div class="form-group">
                        <label class="col-sm-1" style="font-weight:normal">
                           密码:
                        </label>
                        <div class="col-sm-3">
                            <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
                        </div>
                </div>
                  {% if errors %}
                      <span style="color: red">{{ errors.error }}</span>
                  {% endif %}
                <button class="btn btn-info" style="margin-left:113px" type="submit">登陆</button>
                <button class="btn btn-danger" type="reset">清空</button>
              </form>
            </div>
        </div>
      </div>

{% endblock %}

  6.在index首页集成用户登出同时生成动态的菜单链接,点击跳转到相应的页面

  登出

<li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">{{ request.user.name }}</a>
        <ul class="dropdown-menu" role="menu">
            <li><a href="{% url 'account_logout' %}">注销</a></li>
        </ul>
</li>

  动态菜单生成

<div class="container-fluid">
      <div class="row">
        <div class="col-sm-3 col-md-2 sidebar">
          <ul class="nav nav-sidebar">

              {% for role in request.user.roles.all %}
                {% for menu in role.menus.all %}
                    <li>
{# 如果是绝对路径的url type=1,直接显示url的名称,如果是相对路径的url type=0,则动态根据url的别名来获取url #} <a href="{% if menu.url_type == 0 %}{% url menu.url_name %}{% else %}{{ menu.url_name }}{% endif %}">{{ menu.name }}</a> </li> {% endfor %} {% endfor %} </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block page-content %} {% endblock %} </div> </div> </div>
原文地址:https://www.cnblogs.com/luhuajun/p/7929166.html