python 全栈开发,Day117(popup,Model类的继承,crm业务开发)

昨日内容回顾

第一部分:权限相关
    1. 权限基本流程
        用户登录成功后获取权限信息,将【权限和菜单】信息写入到session。
        以后用户在来访问,在中间件中进行权限校验。
        为了提升用户体验友好度,在后台通过inclusion_tag动态生成一个二级菜单。
    2. 使用权限
        - 用户登陆:权限和菜单的初始化; init_permission
        - 配置中间件
        - 配置白名单
        - 配置session中使用到的key 
        - load rbac 
            - menu ,inclusion_tag 生成菜单 
            - filter,可以在if后做条件,粒度控制到按钮。
第二部分:stark组件
    1. 如何使用
        - 在app中编写 stark.py 
        - 在stark.py中进行定制
            - 默认配置: 
                site.register(models.UserInfo)
            - 自定义配置:
                class UserConfig(StarkConfig):    
                    list_display = [] # 表格式列表上显示的字段
                    def get_list_display():
                        pass 
                    order_by = []     # 排序
                    action_list=[]    # 批量操作
                    search_list = []  # 模糊搜索
                    list_filter = []  # 组合搜索
                    add_btn           # 是否显示添加按钮
                    model_form_class  # 自定义ModelForm 
                    
                    def extra_url(self):  # 自定义扩展 URL 
                        pass 
                        
                    def get_urls(self):   # 自定义URL 
                        pass 
                        
                    def changelist_view(self,request):
                        pass 
                        
                    def add_view(self,request):
                        pass 
                        
                    def change_view(self,request):
                        pass 
                        
                    def del_view(self,request):
                        pass 
                    
                site.register(models.UserInfo,UserConfig)
View Code

一、popup

什么是popup

popup英文翻译叫 弹出窗口。

弹窗是一个非常流行的对话框,弹窗可以覆盖在页面上展示。

弹窗可用于显示一段文本,图片,地图或其他内容。

注意:popup弹窗,是由浏览器生成的!

在前端里面的,有一个模态框。那是由Html生成的!跟popup弹框不一样!

window.open

popup实际上,是调用了js中的window.open方法

open() 方法用于打开一个新的浏览器窗口或查找一个已命名的窗口。

语法

window.open(URL,name,features,replace)

注意:如果name重名,只会打开一个

窗口特征(Window Features)

这里只列举,下面例子中,会用到的参数

status=yes|no|1|0 是否添加状态栏。默认是 yes。
height=pixels 窗口文档显示区的高度。以像素计。
width=pixels 窗口的文档显示区的宽度。以像素计。
toolbar=yes|no|1|0 是否显示浏览器的工具栏。默认是 yes。
resizable=yes|no|1|0 窗口是否可调节尺寸。默认是 yes。

其他更多参数,请参考链接:

http://www.w3school.com.cn/jsref/met_win_open.asp

在django admin中,就用到了popup。比如之前写的博客系统

点击加号

 它弹出了一个网页框。注意:它是单页面的!不能像浏览器一样,在当前页面,新开一个窗口。

不能收藏!URL地址也不能修改!

举例:

新建一个项目,注意:django版本为1.11

 修改urls.py,增加路径

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]
View Code

修改views.py,增加视图函数

from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request,"index.html")
View Code

在templates目录下,创建文件index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>添加页面</h1>
<form>
    <p><input type="text"></p>
    <p>
        <select id="city">
            <option>北京</option>
            <option>上海</option>
            <option value="1">深圳</option>
        </select>
        <input type="button" value="+" onclick="popUp();">
    </p>
</form>
<script>
    function popUp() {
        {#弹窗#}
        window.open("http://www.py3study.com/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0")
    }
</script>
</body>
</html>
View Code

启动django项目,访问首页

点击加号按钮,弹窗页面。它是一个单页面!

弹窗的内容,可以定制吗?当然可以!

修改urls.py,增肌路径

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^pop/', views.pop),
]
View Code

修改views.py,增肌视图函数

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    return render(request,"index.html")

def pop(request):
    if request.method == "GET":
        return render(request,"add_city.html")
    
    print(request.POST)
    return HttpResponse('添加成功')
View Code

在templates目录下,创建文件add_city.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>创建城市</h1>
    <form method="post">
        {% csrf_token %}
        <div style=" 200px;height: 300px;border: 1px solid #dddddd">
            <input type="text" name="city">
            <input type="submit" value="提交">
        </div>
    </form>
</body>
</html>
View Code

刷新页面,重新点击加号按钮,效果如下:

 

点击提交之后,效果如下:

但是窗口并没有自动关闭!它应该自动关闭,并跳转到首页才对!

怎么让它自动关系呢?使用windows.close()

由于windows.close()是js代码,需要使用html文件来执行才行!

在templates目录下,创建文件pop_response.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>正在关闭</title>
</head>
<body>
    <script>
        {#自执行函数#}
        (function () {
            window.close();
        })()
    </script>
</body>
</html>
View Code

自执行函数,也就是能够自动立即执行的函数

看下面一段代码

(function () {
    window.close();
})()

在js中声明函数,使用function关键字。函数执行,必须要加括号执行。

所以上面一段代码,就是声明之后,立刻被执行了!

还有一种用途,用于做隔离。比如多个js文件之间相互调用时!

修改views.py,渲染pop_response.html

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    return render(request,"index.html")

def pop(request):
    if request.method == "GET":
        return render(request,"add_city.html")

    print(request.POST)
    return render(request, 'pop_response.html')
View Code

重启django,刷新页面,重新添加一次。效果如下:

那么问题来了,添加之后的数据,要在下拉框中展示。要实时更新,怎么搞?

注意:拉下框中的数据,是来源于数据库的。所以即使添加成功了,要刷新页面,才能加载出来!

popup,它能记住是由哪个页面触发弹窗的。它能调用原始页面的数据!使用opener调用

测试一下

修改index.html,增加tx方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>添加页面</h1>
<form>
    <p><input type="text"></p>
    <p>
        <select id="city">
            <option>北京</option>
            <option>上海</option>
            <option value="1">深圳</option>
        </select>
        <input type="button" value="+" onclick="popUp();">
    </p>
</form>
<script>
    {#测试函数#}
    function tx() {
        alert('xxx');
    }
    function popUp() {
        {#弹窗#}
        window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0")
    }
</script>
</body>
</html>
View Code

修改pop_response.html,调用tx方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>正在关闭</title>
</head>
<body>
    <script>
        {#自执行函数#}
        (function () {
            //调用tx方法
            opener.tx();
            window.close();
        })()
    </script>
</body>
</html>
(function () {
    window.close();
})()
View Code

刷新页面,重新添加一次,效果如下:

那么既然可以调用index.html的方法,就可以传值了

修改views.py,增加变量content,用来使用js传参给index.html

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    return render(request,"index.html")

def pop(request):
    if request.method == "GET":
        return render(request,"add_city.html")

    print(request.POST)
    # 假设数据已经添加成功了,这里要获取添加的id和title
    content = {'id':4,'title':'成都'}
    # 渲染页面,用来将参数传给index.html
    return render(request, 'pop_response.html',content)
View Code

修改pop_response.html,调用tx方法,并传参。

注意:参数是字符串,要用引号括起来

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>正在关闭</title>
</head>
<body>
    <script>
        {#自执行函数#}
        (function () {
            //调用tx方法,参数必须是字符串
            opener.tx('{{ id }}','{{ title }}');
            window.close();
        })()
    </script>
</body>
</html>
(function () {
    window.close();
})()
View Code

修改index.html,接收参数后,操作DOM

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>添加页面</h1>
<form>
    <p><input type="text"></p>
    <p>
        <select id="city">
            <option value="1">北京</option>
            <option value="2">上海</option>
            <option value="3">深圳</option>
        </select>
        <input type="button" value="+" onclick="popUp();">
    </p>
</form>
<script>
    {#测试函数#}
    function tx(cityId,cityTitle) {
        //创建option标签
        var tag = document.createElement('option');
        //设置value
        tag.value = cityId;
        // 设置text属性
        tag.innerText = cityTitle;
        // 获取ID标签
        var city = document.getElementById('city');
        //最后一个位置添加option标签
        city.appendChild(tag);
    }
    function popUp() {
        {#弹窗#}
        window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0")
    }
</script>
</body>
</html>
View Code

重启django,刷新页面,重新添加一次!效果如下:

注意:这里只是添加到浏览器了,并没有到数据库。所以刷新页面,数据会丢失!

django render传输的变量。变量只会在指定的文件渲染!如果这个文件包含了js,那么即使js使用{{  }} 语法,也不会渲染!

举例:

修改views.py,给index.html传一个id参数

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    return render(request,"index.html")

def pop(request):
    if request.method == "GET":
        return render(request,"add_city.html",{'id':'33'})

    print(request.POST)
    # 假设数据已经添加成功了,这里要获取添加的id和title
    content = {'id':4,'title':'成都'}
    # 渲染页面,用来将参数传给index.html
    return render(request, 'pop_response.html',content)
View Code

在app01目录下,创建static目录,在此目录下创建test.js

alert('{{id}}');

修改index.html,添加h6标签,引入js文件

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {#测试js文件#}
    <script src="{% static 'test.js' %}"></script>
</head>
<body>
<h1>添加页面</h1>
<h6>'{{ id }}'</h6>
<form>
    <p><input type="text"></p>
    <p>
        <select id="city">
            <option value="1">北京</option>
            <option value="2">上海</option>
            <option value="3">深圳</option>
        </select>
        <input type="button" value="+" onclick="popUp();">
    </p>
</form>
<script>
    {#测试函数#}
    function tx(cityId,cityTitle) {
        //创建option标签
        var tag = document.createElement('option');
        //设置value
        tag.value = cityId;
        // 设置text属性
        tag.innerText = cityTitle;
        // 获取ID标签
        var city = document.getElementById('city');
        //最后一个位置添加option标签
        city.appendChild(tag);
    }
    function popUp() {
        {#弹窗#}
        window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0")
    }
</script>
</body>
</html>
View Code

重启django,刷新页面

发现id没有被渲染出来

但是index.html渲染出来了

在window.open中,name重名,只会打开1个

如果定义了多个window.open,会被浏览器拦截

只会弹出第一个,后续的都会被拦截!

总结:

主页面:
    function xxxxxx(){
        
    }
    window.open('url','name','.....')
    
popup页面:
    opener.xxxxxx()
    // widown.close()
    
补充:
    js的自执行函数
        用于做隔离:
        (function(jq){
            jq.xxx
        })(jQuery)
View Code

二、Model类的继承

Django有三种继承的方式:

  • 抽象基类:被用来继承的模型被称为Abstract base classes,将子类共同的数据抽离出来,供子类继承重用,它不会创建实际的数据表;
  • 多表继承:Multi-table inheritance,每一个模型都有自己的数据库表;
  • 代理模型:如果你只想修改模型的Python层面的行为,并不想改动模型的字段,可以使用代理模型。

注意!同Python的继承一样,Django也是可以同时继承两个以上父类的!

关于这3种继承的方式,详情请参考链接:

https://www.cnblogs.com/feixuelove1009/p/8420751.html

本文主要讲解 多表继承

多表继承

这种继承方式下,父类和子类都是独立自主、功能完整、可正常使用的模型,都有自己的数据库表,内部隐含了一个一对一的关系。

举例

修改models.py

from django.db import models

# Create your models here.
class Author(models.Model):  # 作者
    name=models.CharField(verbose_name="姓名",max_length=32)
    age=models.IntegerField(verbose_name="年龄")

class AuthorDetail(Author):  # 作者详情
    gf=models.CharField(verbose_name="女朋友",max_length=32)
    tel=models.CharField(verbose_name="作者电话",max_length=32)
View Code

它等同于原生sql语句

CREATE TABLE "Author" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(32) NOT NULL,
    "age" int(11) NOT NULL
);

CREATE TABLE "AuthorDetail" (
    "author_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "Author" ("id"),
    "gf" varchar(32) NOT NULL,
    "tel" varchar(32) NOT NULL
);
View Code

使用2个命令,生成表

python manage.py makemigrations
python manage.py migrate

使用Navicat打开数据库,查看表结构,并添加数据

author表

authordetail表

父类和子类都生成了单独的数据表,authordetail中存储了author的id,也就是通过OneToOneField链接在一起。继承关系通过表的JOIN操作来表示。在JPA中称作JOINED。这种方式下,每个表只包含类中定义的字段,不存在字段冗余,但是要同时操作子类和所有父类所对应的表。

author 里面的所有字段在 authordetail 中也是有效的,只不过数据保存在另外一张数据表当中。所以下面两个语句都是可以运行的:

models.Author.objects.filter(name='xiao')
models.AuthorDetail.objects.filter(name='xiao')

修改urls.py

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
]
View Code

修改views.py

from django.shortcuts import render,HttpResponse
from app01 import models

# Create your views here.
def index(request):
    obj1 = models.Author.objects.filter(name='xiao').values()
    obj2 = models.AuthorDetail.objects.filter(name='xiao').values()
    print(obj1)
    print(obj2)
    return HttpResponse('ok')
View Code

重启django,访问首页

查看Pycharm控制台输出:

<QuerySet [{'age': 23, 'name': 'xiao', 'id': 1}]>
<QuerySet [{'gf': '韩雪', 'author_ptr_id': 1, 'tel': '2345', 'age': 23, 'name': 'xiao', 'id': 1}]>

可以看出,authordetail 表打印出本表的字段以及父表author表的字段

如果你有一个 author,那么它同时也是一个 authordetail, 那么你可以使用子 model 的小写形式从 author 对象中获得与其对应的 authordetail 对象:

修改views.py

from django.shortcuts import render,HttpResponse
from app01 import models

# Create your views here.
def index(request):
    obj1 = models.Author.objects.filter(name='xiao').first()
    print(obj1.authordetail)
    return HttpResponse('ok')
View Code

重启django,访问首页

查看Pycharm控制台输出:

AuthorDetail object

但是,如果上例中的 obj1 并不是 authordetail (比如它仅仅只是 author 对象,或者它是其他类的父类),那么在引用 p.authordetail 就会抛开RelatedObjectDoesNotExist: Author has no authordetail. 异常:

def index(request):
    obj1 = models.Author.objects.create(name='zhang',age='25')
    print(obj1.authordetail)
    return HttpResponse('ok')

也就是说,创建author实例的同时不会创建authordetail,但是创建authordetail实例的同时会创建author实例:

修改views.py

from django.shortcuts import render,HttpResponse
from app01 import models

# Create your views here.
def index(request):
    obj1 = models.AuthorDetail.objects.create(name="zhang",age="25",gf="蒋婷婷",tel="2534")
    obj2 = models.Author.objects.filter(name="zhang").values()
    print(obj2)
    return HttpResponse('ok')
View Code

刷新页面,查看Pycharm控制体输出:

<QuerySet [{'age': 25, 'name': 'zhang', 'id': 2}]>

使用Navicat打开2个表

author表

authordetail表

三、crm业务开发

新建一个项目pro_crm,应用名为crm,注意:django版本为1.11

拷贝stark app

下载代码:

链接:https://pan.baidu.com/s/1fLOGH_3G7hPTvCYKX84UdQ 密码:m8rh

将里面的stark目录拷贝至 项目根目录中

注册stark app

修改settings.py,修改INSTALLED_APPS配置项

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'crm.apps.CrmConfig',
    'stark.apps.StarkConfig',
]
View Code

使用stark组件

在目标app的根目录中创建stark.py

进入crm目录,创建stark.py

修改 crm-->models.py,增加表

# from rbac.models import UserInfo as RbacUserInfo
from django.db import models


class Department(models.Model):
    """
    部门表
    """
    title = models.CharField(verbose_name='部门名称', max_length=16)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    员工表
    """
    name = models.CharField(verbose_name='真实姓名', max_length=16)
    phone = models.CharField(verbose_name='手机号', max_length=32)

    gender_choices = (
        (1,''),
        (2,''),
    )
    gender = models.IntegerField(verbose_name='性别',choices=gender_choices,default=1)

    depart = models.ForeignKey(verbose_name='部门', to="Department")

    def __str__(self):
        return self.name


class Course(models.Model):
    """
    课程表
    如:
        Linux基础
        Linux架构师
        Python自动化
        Python全栈
    """
    name = models.CharField(verbose_name='课程名称', max_length=32)

    def __str__(self):
        return self.name


class School(models.Model):
    """
    校区表
    如:
        北京昌平校区
        上海浦东校区
        深圳南山校区
    """
    title = models.CharField(verbose_name='校区名称', max_length=32)

    def __str__(self):
        return self.title


class ClassList(models.Model):
    """
    班级表
    如:
        Python全栈  面授班  5期  10000  2017-11-11  2018-5-11
    """
    school = models.ForeignKey(verbose_name='校区', to='School')
    course = models.ForeignKey(verbose_name='课程名称', to='Course')
    semester = models.IntegerField(verbose_name="班级(期)") # 11
    price = models.IntegerField(verbose_name="学费")
    start_date = models.DateField(verbose_name="开班日期")
    graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True)
    tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',limit_choices_to={'depart__title':'教质部'})
    teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='teach_classes',limit_choices_to={'depart_id__in':[6,7]})
    memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True)

    def __str__(self):
        return "{0}({1}期)".format(self.course.name, self.semester)
View Code

使用2个命令,生成表

python manage.py makemigrations
python manage.py migrate

修改crm-->stark.py,注册表Department

from stark.service.stark import site
from crm import models

site.register(models.Department)
View Code

配置路由信息

修改urls.py,增加stark路由

from django.conf.urls import url
from django.contrib import admin
from stark.service.stark import site

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 导入stark组件的路由
    url(r'^stark/', site.urls),
]
View Code

启动django项目,访问url: http://127.0.0.1:8000/stark/crm/department/list/

默认是没有数据的,需要添加!



 由于时间关系,详细的步骤略...

完整代码,请参考

链接:https://pan.baidu.com/s/1K4YG5LY89aidRWK7j51DBg 密码:30jt

未完待续...

原文地址:https://www.cnblogs.com/xiao987334176/p/9581125.html