Django—CRM项目练习

一、准备

1.创建django项目 命名为my_crm

2.配置app 命名为 crm

3.创建数据库 mysql : create database my_crm; 

4.setting配置

  • static配置
# setting文件

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static")
]
View Code
  • 注册app
# setting.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'crm.apps.CrmConfig',  # <---- 注册app
]
View Code
  •  配置数据库信息
# setting.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'my_crm',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '123456'
    }
}
View Code
# my_crm 下的 __init__.py 

import pymysql
pymysql.install_as_MySQLdb()
View Code
  • 还有一些其他配置,在使用时再进行配置(中间件注册等)

二、数据表建立

我们在配置好setting后要建立类与数据表的对应关系,称为ORM-对象关系映射

示例 : 培训机构的客户管理系统

# models.py文件

from django.db import models
from multiselectfield import MultiSelectField

course_choices = (('Linux', 'Linux中高级'),
                  ('PythonFullStack', 'Python高级全栈开发'),)

class_type_choices = (('fulltime', '脱产班',),
                      ('online', '网络班'),
                      ('weekend', '周末班',),)

source_type = (('qq', "qq群"),
               ('referral', "内部转介绍"),
               ('website', "官方网站"),
               ('baidu_ads', "百度推广"),
               ('office_direct', "直接上门"),
               ('WoM', "口碑"),
               ('public_class', "公开课"),
               ('website_luffy', "路飞官网"),
               ('others', "其它"),)

enroll_status_choices = (('signed', "已报名"),
                         ('unregistered', "未报名"),
                         ('studying', '学习中'),
                         ('paid_in_full', "学费已交齐"))

seek_status_choices = (('A', '近期无报名计划'), ('B', '1个月内报名'), ('C', '2周内报名'), ('D', '1周内报名'),
                       ('E', '定金'), ('F', '到班'), ('G', '全款'), ('H', '无效'),)

pay_type_choices = (('deposit', "订金/报名费"),
                    ('tuition', "学费"),
                    ('transfer', "转班"),
                    ('dropout', "退学"),
                    ('refund', "退款"),)

attendance_choices = (('checked', "已签到"),
                      ('vacate', "请假"),
                      ('late', "迟到"),
                      ('absence', "缺勤"),
                      ('leave_early', "早退"),)

score_choices = ((100, 'A+'),
                 (90, 'A'),
                 (85, 'B+'),
                 (80, 'B'),
                 (70, 'B-'),
                 (60, 'C+'),
                 (50, 'C'),
                 (40, 'C-'),
                 (0, ' D'),
                 (-1, 'N/A'),
                 (-100, 'COPY'),
                 (-1000, 'FAIL'),)


class Department(models.Model):
    """
    部门表
    """
    name = models.CharField(max_length=32, verbose_name="部门名称")
    count = models.IntegerField(verbose_name="人数", default=0)

    def __str__(self):
        return self.name
class UserProfile(models.Model):
    """
    用户表
    """
    username = models.EmailField(max_length=255, unique=True, )
    password = models.CharField(max_length=128)
    name = models.CharField('名字', max_length=32)
    department = models.ForeignKey('Department', default=None, blank=True, null=True)
    mobile = models.CharField('手机', max_length=32, default=None, blank=True, null=True)
    memo = models.TextField('备注', blank=True, null=True, default=None)
    date_joined = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return str(self.id)
class Customer(models.Model):
    """
    客户表
    """
    qq = models.CharField('QQ', max_length=64, unique=True, help_text='QQ号必须唯一')
    qq_name = models.CharField('QQ昵称', max_length=64, blank=True, null=True)
    name = models.CharField('姓名', max_length=32, blank=True, null=True, help_text='学员报名后,请改为真实姓名')
    sex_type = (('male', ''), ('female', ''))
    sex = models.CharField("性别", choices=sex_type, max_length=16, default='male', blank=True, null=True)
    birthday = models.DateField('出生日期', default=None, help_text="格式yyyy-mm-dd", blank=True, null=True)
    phone = models.BigIntegerField('手机号', blank=True, null=True)
    source = models.CharField('客户来源', max_length=64, choices=source_type, default='qq')
    introduce_from = models.ForeignKey('self', verbose_name="转介绍自学员", blank=True, null=True)
    course = MultiSelectField("咨询课程", choices=course_choices)
    class_type = models.CharField("班级类型", max_length=64, choices=class_type_choices, default='fulltime')
    customer_note = models.TextField("客户备注", blank=True, null=True, )
    status = models.CharField("状态", choices=enroll_status_choices, max_length=64, default="unregistered",
                              help_text="选择客户此时的状态")
    last_consult_date = models.DateField("最后跟进日期", auto_now_add=True)
    next_date = models.DateField("预计再次跟进时间", blank=True, null=True)
    consultant = models.ForeignKey('UserProfile', verbose_name="销售", related_name='customers', blank=True, null=True, )
    class_list = models.ManyToManyField('ClassList', verbose_name="已报班级", )

    def __str__(self):
        return str(self.id)

class Campuses(models.Model):
    """
    校区表
    """
    name = models.CharField(verbose_name='校区', max_length=64)
    address = models.CharField(verbose_name='详细地址', max_length=512, blank=True, null=True)

    def __str__(self):
        return self.name
class ClassList(models.Model):
    """
    班级表
    """
    course = models.CharField("课程名称", max_length=64, choices=course_choices)
    semester = models.IntegerField("学期")
    campuses = models.ForeignKey('Campuses', verbose_name="校区")
    price = models.IntegerField("学费", default=10000)
    memo = models.CharField('说明', blank=True, null=True, max_length=100)
    start_date = models.DateField("开班日期")
    graduate_date = models.DateField("结业日期", blank=True, null=True)
    teachers = models.ManyToManyField('UserProfile', verbose_name="老师")
    class_type = models.CharField(choices=class_type_choices, max_length=64, verbose_name='班额及类型', blank=True,
                                  null=True)

    class Meta:
        unique_together = ("course", "semester", 'campuses')

    def __str__(self):
        return "{}{}{}".format(self.campuses,self.course,self.semester)

class ConsultRecord(models.Model):
    """
    跟进记录表
    """
    customer = models.ForeignKey('Customer', verbose_name="所咨询客户")
    note = models.TextField(verbose_name="跟进内容...")
    status = models.CharField("跟进状态", max_length=8, choices=seek_status_choices, help_text="选择客户此时的状态")
    consultant = models.ForeignKey("UserProfile", verbose_name="跟进人", related_name='records')
    date = models.DateTimeField("跟进日期", auto_now_add=True)
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)


class Enrollment(models.Model):
    """
    报名表
    """

    why_us = models.TextField("为什么报名", max_length=1024, default=None, blank=True, null=True)
    your_expectation = models.TextField("学完想达到的具体期望", max_length=1024, blank=True, null=True)
    contract_agreed = models.BooleanField("我已认真阅读完培训协议并同意全部协议内容", default=False)
    contract_approved = models.BooleanField("审批通过", help_text="在审阅完学员的资料无误后勾选此项,合同即生效", default=False)
    enrolled_date = models.DateTimeField(auto_now_add=True, verbose_name="报名日期")
    memo = models.TextField('备注', blank=True, null=True)
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)
    customer = models.ForeignKey('Customer', verbose_name='客户名称')
    school = models.ForeignKey('Campuses')
    enrolment_class = models.ForeignKey("ClassList", verbose_name="所报班级")

    class Meta:
        unique_together = ('enrolment_class', 'customer')


class PaymentRecord(models.Model):
    """
    缴费记录表
    """
    pay_type = models.CharField("费用类型", choices=pay_type_choices, max_length=64, default="deposit")
    paid_fee = models.IntegerField("费用数额", default=0)
    note = models.TextField("备注", blank=True, null=True)
    date = models.DateTimeField("交款日期", auto_now_add=True)
    course = models.CharField("课程名", choices=course_choices, max_length=64, blank=True, null=True, default='N/A')
    class_type = models.CharField("班级类型", choices=class_type_choices, max_length=64, blank=True, null=True,
                                  default='N/A')
    enrolment_class = models.ForeignKey('ClassList', verbose_name='所报班级', blank=True, null=True)
    customer = models.ForeignKey('Customer', verbose_name="客户")
    consultant = models.ForeignKey('UserProfile', verbose_name="销售")
    delete_status = models.BooleanField(verbose_name='删除状态', default=False)

    status_choices = (
        (1, '未审核'),
        (2, '已审核'),
    )
    status = models.IntegerField(verbose_name='审核', default=1, choices=status_choices)

    confirm_date = models.DateTimeField(verbose_name="确认日期", null=True, blank=True)
    confirm_user = models.ForeignKey(verbose_name="确认人", to='UserProfile', related_name='confirms', null=True,
                                     blank=True)


class CourseRecord(models.Model):
    """课程记录表"""
    day_num = models.IntegerField("节次", help_text="此处填写第几节课或第几天课程...,必须为数字")
    date = models.DateField(auto_now_add=True, verbose_name="上课日期")
    course_title = models.CharField('本节课程标题', max_length=64, blank=True, null=True)
    course_memo = models.TextField('本节课程内容', max_length=300, blank=True, null=True)
    has_homework = models.BooleanField(default=True, verbose_name="本节有作业")
    homework_title = models.CharField('本节作业标题', max_length=64, blank=True, null=True)
    homework_memo = models.TextField('作业描述', max_length=500, blank=True, null=True)
    scoring_point = models.TextField('得分点', max_length=300, blank=True, null=True)
    re_class = models.ForeignKey('ClassList', verbose_name="班级")
    teacher = models.ForeignKey('UserProfile', verbose_name="讲师",related_name="teacher" )
    recorder = models.ForeignKey('UserProfile', verbose_name="记录者")

    class Meta:
        unique_together = ('re_class', 'day_num')


class StudyRecord(models.Model):
    """
    学习记录
    """

    attendance = models.CharField("考勤", choices=attendance_choices, default="checked", max_length=64)
    score = models.IntegerField("本节成绩", choices=score_choices, default=-1)
    homework_note = models.CharField(max_length=255, verbose_name='作业批语', blank=True, null=True)
    date = models.DateTimeField(auto_now_add=True)
    note = models.CharField("备注", max_length=255, blank=True, null=True)
    homework = models.FileField(verbose_name='作业文件', blank=True, null=True, default=None)
    course_record = models.ForeignKey('CourseRecord', verbose_name="某节课程")
    student = models.ForeignKey('Customer', verbose_name="学员")

    class Meta:
        unique_together = ('course_record', 'student')
View Code

之后向数据库提交

# 打开cmd 输入:

python3 manage.py makemigrations
python3 manage.py migrate
View Code

此时,打开数据库查询my_crm下的表,就能看到我们添加的表啦~

三、登录、注册功能

  1.urls配置 (应用路由分配)

    1. 在crm(app)下创建urls.py文件,文件内容复制my_crm下urls.py的内容
    2. my_crm下的urls.py如下
      # my_crm 下的 urls.py
      
      from django.conf.urls import url, include
      from django.contrib import admin
      
      urlpatterns = [
          url(r'^admin/', admin.site.urls),
          url(r'^crm/', include('crm.urls', namespace='crm')),
      
      ]
      View Code
    3. crm下的urls.py如下 

      # crm 下的url
      
      from django.conf.urls import url
      from crm import views
      
      urlpatterns = [
          url(r'^login/', views.login, name='login'),
          url(r'^reg/', views.reg, name='reg'),
      ]
      View Code 

  2.views

    由于我们不知道login和reg页面会传回哪些值,所以先将views中login方法和reg方法写成如下形式

from django.shortcuts import render

def login(request):
    return render(request,"login.html")


def reg(request):
    return render(request,"reg.html")
View Code

  3.HTML页面

    步骤:

1.这个步骤需要完成的功能有两个,一是展示登录/注册界面,二是将用户提交的值传回views交给对应的函数处理

2.我们先制作reg页面,查看models.py中的用户表,我们发现需要的字段有username、password、name三项必填项,其余均为选填项,可在后续添加完善个人信息功能,进行其他资料的填写

3.我们可以在后端调用django的form组件来生成HTML页面的input标签,所以现在crm下创建forms.py文件,内容如下

from django import forms

class RegForm(forms.ModelForm):
    password = forms.CharField(min_length=6,
                               widget=forms.PasswordInput(attrs={'placeholder': '密码', 'autocomplete': 'off'}))
   # 添加一个确认密码的字段 
    re_password = forms.CharField(min_length=6,
                                  widget=forms.PasswordInput(attrs={'placeholder': '确认密码', 'autocomplete': 'off'}))

    class Meta:
        model = models.UserProfile
        fields = '__all__'  # ['username'] 展示的内容,也可写成列表形式,__all__代表展示全部
     # exclude 后的字段不进行展示
        exclude = ['is_active'] # is_active 是管理员级别的字段,默认是True,判断用户是否在职,一旦用户离职,便可以将该值设为False,禁止登陆管理系统

        widgets = {
            'username': forms.TextInput(attrs={'placeholder': '用户名', 'autocomplete': 'off'}),
            'password': forms.PasswordInput(attrs={'placeholder': '密码', 'autocomplete': 'off'}),
            'name': forms.TextInput(attrs={'placeholder': '真实姓名', 'autocomplete': 'off'}),
            'mobile': forms.TextInput(attrs={'placeholder': '手机号', 'autocomplete': 'off'}),

        }
     # labels 可以设置各input框的label值
        labels = {
            'username': 'xxx'
        }
     # error_message 是form组件校验功能的一部分,它可以将用户输入的错误在前端展示出来
        error_messages = {
            'username': {}
        }
  # 全局钩子,验证两次密码是否一致
    def clean(self):
        self._validate_unique = True  # 到数据库校验唯一
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            md5 = hashlib.md5()
            md5.update(password.encode('utf-8'))
            self.cleaned_data['password'] = md5.hexdigest()
            return self.cleaned_data
        self.add_error('re_password', '两次密码不一致!!')
        raise ValidationError('两次密码不一致')
View Code

4.在写好forms.py后,我们先将views内的reg函数完善,内容如下:

View Code

 5.当发起get请求时reg函数向reg.html传了一个form_obj,我们利用这个将html页面完善(有一些样式信息,不要混淆)

    <form action="" method="post" novalidate>
        {% csrf_token %}
        <div>
            {{ form_obj.username }}
            {{ form_obj.username.errors.0 }}
        </div>
        <div>
            {{ form_obj.password }}
           {{ form_obj.password.errors.0 }}
        </div>
        <div>{{ form_obj.re_password }} {{ form_obj.re_password.errors.0 }}</div>
        <div>{{ form_obj.name }} {{ form_obj.name.errors.0 }}</div>
        <div>{{ form_obj.department }} {{ form_obj.department.errors.0 }}</div>
        <div>{{ form_obj.mobile }} {{ form_obj.mobile.errors.0 }}</div>

        <div>{{ error }} </div>

        <button id="submit">Sign up</button>
     </form>
View Code

6.注册功能已经实现,我们来设计登录功能, 由于登录我们只需要验证用户名和密码是否在数据库中存在且一致,无需其他验证,所以我们可以不适用form组件实现,完善views中的login方法

def login(request):
    if request.method == 'POST':
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        md5 = hashlib.md5() # 调用hashlab中的md5,对密码进行加密
        md5.update(pwd.encode('utf-8'))
        pwd = md5.hexdigest()
        obj = models.UserProfile.objects.filter(username=user, password=pwd, is_active=True).first()
        if obj:
            # session保存用户的id 登陆状态
            request.session['user_id'] = obj.pk
            request.session['is_login'] = '1'

            return redirect('crm:customer_list')
        return render(request, 'login.html', {'error': '用户名或密码错误'})
    return render(request, 'login.html')
View Code

7.完善html界面

    <form action="" method="post">
        {% csrf_token %}
        <div>
            <input type="text" name="username" class="username" placeholder="Username" autocomplete="off">
        </div>
        <div>
            <input type="password" name="password" class="password" placeholder="Password" οncοntextmenu="return false"
                   onpaste="return false">
        </div>
        <div>{{ error }} </div>

        <button id="submit">Sign in</button>
    </form>
View Code

 四、展示数据

1.展示数据分为公户数据和私户数据,就是每个销售要有自己的资源,他们可以将公共资源申请为己有单独对接,也可将不再跟进的客户提交到公户,让其他销售对接,公户展示和私户展示本质上的区别就是客户表中对应的销售字段。

2.销售字段为None时就是公户,其余均为私户。

3.无论公户和私户都需要登录后才能查看,用户只能查看到自己的私户数据,并不能查看到他人的私户数据

逻辑:

  1.当用户登陆后,直接展示公户界面,界面中含有超链接可跳转到私户展示

  2.登录验证我们要用到session,设置一个中间件,保存登录的用户对象,这样后面展示私户的时候就可以调用了

  3.公户展示和私户展示后端的逻辑是一样的,只有一个字段的差别,所以我们可以用一个函数来实现这个功能,通过url的不同来判定走那个分支

实际操作:

  1.我们先写出用户是否登录验证的中间件,在crm(app)下创建文件夹,通常命名为middlewares,在这个文件夹中创建一个py文件,名称随意,这里将它命名为my_middleware,在这个文件夹下就可以写我们自定义的中间件了,具体代码如下

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, reverse
from crm import models


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 白名单 :因为我们的登陆和注册页面是不需要认证的,所以我们将这两个地址排除
        url = request.path_info
        if url in [reverse('crm:login'), reverse('crm:reg')]:
            return
     # django自带的admin组件也是不需要认证的,所以我们将admin开头的全部页面都排除
        if url.startswith('/admin'):
            return
      
        # 获取登陆状态
     is_login = request.session.get('is_login')
     if is_login != '1':
        # 跳转登陆页面
        return redirect('crm:login')
      # 查询用户对象  
     user_obj = models.UserProfile.objects.filter(pk=request.session.get('user_id'))
     request.user_obj = user_obj  # request.user
View Code

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/Asang47/p/11544868.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值