Python之路【第十七篇】Django进阶篇

规范

确立规范的好处

  1. 代码可读性高
  2. 方便代码的定位极其查找
  3. 为以后代码扩容带来便利

场景:

在多个APP的场景下,单个app的URL函数功能较多的时候,我们可以通过以下方法来解决。

把Views写成模块的方式并且为不同的功能进行不同的划分,并且在Templates中使用同样规则,如下图:

我根据不同的html然后创建不同的函数,命名和templates模板目录一样这样非常方便找到,这个页面中的函数在哪里。

设置路由的时候就得导入相应的函数(下面的函数是在app01中的url,通过object的url转发过来):

复制代码
from cmdb.views import account
from cmdb.views import home
from cmdb.views import asset

urlpatterns = [

    #账户操作登录登出
    url(r'^login/$', account.login),
    url(r'^logout/$', account.logout),

    #home操作
    url(r'^index/$', home.index),

    #资产信息操作
    url(r'^lists/$', asset.lists),
    url(r'^save_hostinfo/$', asset.save_hostinfo),
    url(r'^del_hostinfo/$', asset.del_hostinfo),
    url(r'^add/$', asset.add),

    #default url
    url(r'', account.login),

]
复制代码

还有就是Form也是有很多文件的所以我们也要单独创建下!

2、静态文件导入优化

看下面的一种情况首先咱们在导入静态的css和js的时候是怎么导入的?

复制代码
{#CSS#}
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet"  href="/static/plugins/bootstrap/css/bootstrap.min.css"/>
    <link rel="stylesheet"  href="/static/plugins/font-awesome/css/font-awesome.min.css"/>
    <link rel="stylesheet"  href="/static/css/commons.css"/>
    <link rel="stylesheet"  href="/static/css/account.css"/>
</head>


{#JS#}
<script src='/static/js/jquery-1.8.2.min.js'></script>
<script src='/static/js/valid.js'></script>
复制代码

这个static在哪里设置的呢?在object的settings里设置的:

复制代码
########### 静态文件路径 ##########
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

########### 增加'django.core.context_processors.static', 在OPTIONS里

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'django.core.context_processors.static',
            ],
        },
    },
]


‘’‘
##查看原理##
'django.core.context_processors.static',

def static(request):
    """
    Adds static-related context variables to the context.
    """
    return {'STATIC_URL': settings.STATIC_URL} #这里和模板语言一样返回一个字典


’‘’
复制代码

那如果以后想把static目录改成content怎么改呢?难道要在每个html中去修改路径吗?这样如果html文件多的话会疯的,所以我们可以这么修改:

如果想修改路径的话直接修改:settings里的   STATIC_URL = '/static/' 指定新的目录即可

复制代码
{#CSS#}
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet"  href="{{ STATIC_URL }}plugins/bootstrap/css/bootstrap.min.css"/>
    <link rel="stylesheet"  href="{{ STATIC_URL }}plugins/font-awesome/css/font-awesome.min.css"/>
    <link rel="stylesheet"  href="{{ STATIC_URL }}css/commons.css"/>
    <link rel="stylesheet"  href="{{ STATIC_URL }}css/account.css"/>
    <link rel="stylesheet"  href="{{ STATIC_URL }}css/login.css"/>
</head>

{#JS#}
<script src='{{ STATIC_URL }}js/jquery-2.1.4.min.js'></script>
<script src='{{ STATIC_URL }}js/valid.js'></script>
复制代码

上面的原理就类似模板语言的渲染一样!

效果如下:

2、还有一种方式

通过simple_tag 的方式进行导入,他不需要注册option即可。注意里面的路径

{#在顶部load 一个staticfiles#}
{% load staticfiles %}


{#在底部使用#}
<script src='{% static "js/jquery-2.1.4.min.js" %}'></script>

Form

首先咱们看下,咱们在Form里和Views里都使用了account很容易造成混乱所以我们可以使用别名来区分:

from cmdb.forms import account as AccountForm

1、Form保存用户输入内容

场景:比如有一个html页面有很多的input标签需要你输入,但是如果你输入错误了,数据已经提交过去了,所有数据需要你在写一遍是不是非常不人性化,看下面的例子:(我故意输入错误了密码之后信息还需要我在重新写一遍)

代码如下:

复制代码
def login(request):
    obj = AccountForm.LoginForm()
    if request.method == 'POST':
        #获取用户输入
        login_form = AccountForm.LoginForm(request.POST)
        #判断用户输入是否合法
        if login_form.is_valid():#如果用户输入是合法的
            username = request.POST.get('username')
            password = request.POST.get('password')
            user_info_list = models.UserInfo.objects.all()
            for i in user_info_list:
                if username == i.email and password == i.passwords:
                    request.session['auth_user'] = username
                    return redirect('/index/')
            else:
                return render(request,'account/login.html',{'model': obj,'backend_autherror':'用户名或密码错误'})
        else:
            error_msg = login_form.errors.as_data()
            return render(request,'account/login.html',{'model': obj,'errors':error_msg})

    # 如果登录成功,写入session,跳转index
    return render(request, 'account/login.html', {'model': obj})
复制代码

优化的方法就是:

复制代码
def login(request):
    #获取用户输入
    login_form = AccountForm.LoginForm(request.POST)
    if request.method == 'POST':
        #判断用户输入是否合法
        if login_form.is_valid():#如果用户输入是合法的
            username = request.POST.get('username')
            password = request.POST.get('password')
            user_info_list = models.UserInfo.objects.all()
            for i in user_info_list:
                if username == i.email and password == i.passwords:
                    request.session['auth_user'] = username
                    return redirect('/index/')
            else:
                return render(request,'account/login.html',{'model': login_form,'backend_autherror':'用户名或密码错误'})
        else:
            error_msg = login_form.errors.as_data()
            return render(request,'account/login.html',{'model': login_form,'errors':error_msg})

    # 如果登录成功,写入session,跳转index
    return render(request, 'account/login.html', {'model': login_form})
复制代码

这里,我们把用户输入的请求封装到Form表单给用户返回回去了!这样用户提交的数据就可以在同样返回给用户!

那有人会问如果不是POST的时候,你也这样返回没有问题吗?没有问题的,没有POST请求的话request.POST是空所以不影响。

效果如下:

2、Form错误信息

Form的错误信息有3种,咱们常用的就是:

error_msg = errors

error_msg = errors.as_json()

error_msg.as_ul很少用

咱们在返回Ajax的请求的时候能返回对象吗?是不可以的!(咱们一般返回字符串,或字符串类型的字典),但是咱们可以通过error_msg = errors.as_json()来返回!

print type(login_form.errors)
print type(login_form.errors.as_json())

结果:

<class 'django.forms.utils.ErrorDict'>
<type 'str'>

第一个是ErrorDict是Django定义的一个类,第二个是字符串。

看下第一个错误信息,他是一个字典我们可以通过字典来取他的值看下(Views):

error_msg = login_form.errors.as_data()
print error_msg['username'][0]
print error_msg['password'][0]

然后当我们发送数据的时候通下面的方式发送在模板中我们不能通过字典的方式取值只能通过“.”的方式取字典的值怎么办?所以就需要simple_tag来做了:

return render(request,'account/login.html',{'model': login_form,'errors':error_msg})
复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from django import template
from django.utils.safestring import mark_safe
from django.template.base import resolve_variable, Node, TemplateSyntaxError

register = template.Library()

@register.simple_tag
def error_message(arg):
    if arg: 
        return arg[0][0]
    else:
        return ''
复制代码

2、对于Ajax来说

error_msg = login_form.errors.as_json()
print error_msg,type(error_msg)
{
"username": [{"message": "\u90ae\u7bb1\u8d26\u6237\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}], 
"password": [{"message": "\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a", "code": "required"}]} 
<type 'str'>

之前说过的在error的时候有多个错误,我们默认取第0个就可以了,我们在通过Ajax发送登录认证的时候可以通过返回来的错误信息通过js来进行显示即可!

3、FormSelect更新

看下面的这种情况基本的通过form生成select标签

html

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>可更新下拉菜单</h1>
    <p>{{ obj.host }}</p>
</body>
</html>
复制代码

form

复制代码
class ImportForm(forms.Form):
    HOST_TYPE_LIST = (
        (1,'物理机'),
        (2,'虚拟机'),
    )

    host = forms.IntegerField(
        widget=forms.Select(choices=HOST_TYPE_LIST)
    )
复制代码

views

def index(request):
    obj = HomeForm.ImportForm(request.POST)
    return render(request,'home/index.html',{'obj':obj})

这个select里面的选项是写死的就两个选项他是静态的,如果这个是业务线的话,是动态的话,我有新增了一个业务线怎么办?先拿文件举例,后面会跟上从数据库取数据的例子!

有一个文件db_admin通过json.dumps生成的文本

[[1, "物理机"], [2, "虚拟机"]]

然后在form里调用,html&views不变,修改下form

复制代码
class ImportForm(forms.Form):

    f = open('db_admin')
    data = f.read()
    data_tuple = json.loads(data)
    f.close()
    host = forms.IntegerField(
        widget=forms.Select(choices=data_tuple)
    )
复制代码

现在是动态生成了,我现在在db_admin里在增加一个字段

[[1, "物理机"], [2, "虚拟机"],[3, "云主机"]]

当我在刷新页面的时候发现:为什么我添加的‘[3,"云主机"]’,为什么没有出现呢?我已经添加了!

说道这个问题就得回顾下之前的面相对象的东西了,静态再断当类创建之后就保存在内存中了,当你在去调用ImportForm的时候他不会再去执行了,所以当调用这个ImportForm类实例化的时候,静态字段是不变的他没有重新读!

意思就是,当他调用的时候还是之前的内容怎么办呢?“重启整个程序让他重新加载类”,太low了吧!

当类在被调用的时候首先执行的是类的构造方法:__init__方法,每当调用类的实例化的时候他都执行!所以我们可以给form定义一个form方法。

复制代码
class ImportForm(forms.Form):
    f = open('db_admin')
    data = f.read()
    data_tuple = json.loads(data)
    f.close()
    host = forms.IntegerField(
        widget=forms.Select(choices=data_tuple) 
    )

    def __init__(self, *args, **kwargs):
        super(ImportForm, self).__init__(*args, **kwargs)
        f = open('db_admin')
        data = f.read()
        data_tuple = json.loads(data)
        f.close()

        #通过self.fields找到host这个静态字段的.wideget.choices属性并重新给他赋值!
        self.fields['host'].widget.choices = data_tuple
复制代码

优化:

复制代码
class ImportForm(forms.Form):
    host = forms.IntegerField(
        widget=forms.Select()  #这里默认是空值,每一次我们使用的时候通过__init__进行赋值
    )

    def __init__(self, *args, **kwargs):
        super(ImportForm, self).__init__(*args, **kwargs) #执行当前类的父类的构造方法(__init__)
        f = open('db_admin')
        data = f.read()
        data_tuple = json.loads(data)
        f.close()

        #通过self.fields找到host这个静态字段的.wideget.choices属性并重新给他赋值!
        self.fields['host'].widget.choices = data_tuple
复制代码

Model

到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞:

  • 创建数据库,设计表结构和字段
  • 使用 MySQLdb 来连接数据库,并编写数据访问层代码
  • 业务逻辑层去调用数据访问层执行数据库操作
复制代码
import MySQLdb
 
def GetList(sql):
    db = MySQLdb.connect(user='root', db='luotianshuai', passwd='1234', host='localhost')
    cursor = db.cursor()
    cursor.execute(sql)
    data = cursor.fetchall()
    db.close()
    return data
 
def GetSingle(sql):
    db = MySQLdb.connect(user='root', db='luotianshuai', passwd='1234', host='localhost')
    cursor = db.cursor()
    cursor.execute(sql)
    data = cursor.fetchone()
    db.close()
    return data
复制代码

首先Django是通过Model操作数据库,不管你数据库的类型是MySql或者Sqlite,Django它自动帮你生成相应数据库类型的SQL语句,所以不需要关注SQL语句和类型,对数据的操作Django帮我们自动完成。只要回写Model就可以了!

1、创建数据库表

django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)

  •   PHP:activerecord
  •   Java:Hibernate 
  •    C#:Entity Framework

django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。

对于ORM框架里:

我们写的表示数据库的表

如果根据这个类创建的对象是数据库表里的一行数据

对象.id 对象.value 是每一行里的数据 

class UserInfo(models.Model):
    username = models.CharField(max_length=64)
    email = models.EmailField(max_length=64,null=True)
    email2 = models.EmailField(max_length=64,default="luotianshuai@qq.com")

2、更多字段

首先要了解,如果Django如果不是有一个后台admin可以操作数据库的话,Model没有必要设置很多的数据类型,那么这么多的数据类型,他的存在的意义就是为了限制从admin时的操作。 

 还有就是不要把Form和Model弄混了!他们两个是完全独立的!Model的数据类型和Form没有关系,数据类型存在的意义就是限制admin的操作。(原因:如果不对admin进行限制的话容易造成脏数据)但是form提交的数据也必须是符合Model的存储要求的,这个可以在form哪里进行判断,然后存储即可。

在admin添加数据的时候,如果在Model设置了规则如下:

复制代码
1、models.AutoField  自增列 = int(11)
  如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。
2、models.CharField  字符串字段
  必须 max_length 参数
3、models.BooleanField  布尔类型=tinyint(1)
  不能为空,Blank=True
4、models.ComaSeparatedIntegerField  用逗号分割的数字=varchar
  继承CharField,所以必须 max_lenght 参数
5、models.DateField  日期类型 date
  对于参数,auto_now = True 则每次更新都会更新这个时间;auto_now_add 则只是第一次创建添加,之后的更新不再改变。
6、models.DateTimeField  日期类型 datetime
  同DateField的参数
7、models.Decimal  十进制小数类型 = decimal
  必须指定整数位max_digits和小数位decimal_places
8、models.EmailField  字符串类型(正则表达式邮箱) =varchar
  对字符串进行正则表达式
9、models.FloatField  浮点类型 = double
10、models.IntegerField  整形
11、models.BigIntegerField  长整形
  integer_field_ranges = {
    'SmallIntegerField': (-32768, 32767),
    'IntegerField': (-2147483648, 2147483647),
    'BigIntegerField': (-9223372036854775808, 9223372036854775807),
    'PositiveSmallIntegerField': (0, 32767),
    'PositiveIntegerField': (0, 2147483647),
  }
12、models.IPAddressField  字符串类型(ip4正则表达式)
13、models.GenericIPAddressField  字符串类型(ip4和ip6是可选的)
  参数protocol可以是:both、ipv4、ipv6
  验证时,会根据设置报错
14、models.NullBooleanField  允许为空的布尔类型
15、models.PositiveIntegerFiel  正Integer
16、models.PositiveSmallIntegerField  正smallInteger
17、models.SlugField  减号、下划线、字母、数字
18、models.SmallIntegerField  数字
  数据库中的字段有:tinyint、smallint、int、bigint
19、models.TextField  字符串=longtext
20、models.TimeField  时间 HH:MM[:ss[.uuuuuu]]
21、models.URLField  字符串,地址正则表达式
22、models.BinaryField  二进制
23、models.ImageField   图片
24、models.FilePathField 文件
复制代码

这里单独说下

models.DateTimeField

复制代码
class UserInfo(models.Model):
    username = models.CharField(max_length=64)
    email = models.EmailField(max_length=64,null=True)
    email2 = models.EmailField(max_length=64,default="luotianshuai@qq.com")
    ctime = models.DateTimeField(auto_now=True) #默认这样写上就不用管了,每当你创建一行数据的时候就会在那一行数据中增加一个ctime字段
    uptime = models.DateTimeField(auto_now_add=True)#默认写成这样也不同管了,当前表任何一行有修改的时候他就会自动更新.
复制代码

他在数据库的存储方式如下:

models.GenericIPAddressField  

他和email类似也是字符串类型然后进行了正则表达式的判断他支持IPV4,IPV6,旧的models.IPAddressField 已经很少用了

models.ImageField 图片
models.FilePathField 文件

复制代码
class UserInfo(models.Model):
    username = models.CharField(max_length=64)
    email = models.EmailField(max_length=64,null=True)
    email2 = models.EmailField(max_length=64,default="luotianshuai@qq.com")
    img = models.ImageField(null=True,blank=True,upload_to='user_upload')
    #null=True,表示数据库存储的时候可以为空,blank=True表示在admin后台提交的时候可以为空!
    #upload_to='user_upload' 用户提交的数据保存到哪里
复制代码

这里需要注意:在数据库中实际保存的并不是文件,而是文件的URL

文件保存:

 

Model扩展知识点(1):输出Model对象默认返回值

复制代码
class UserInfo(models.Model):
    username = models.CharField(max_length=64)
    ctime = models.DateTimeField(auto_now=True) #默认这样写上就不用管了,每当你创建一行数据的时候就会在那一行数据中增加一个ctime字段
    uptime = models.DateTimeField(auto_now_add=True)#默认写成这样也不同管了,当前表任何一行有修改的时候他就会自动更新.

    def __unicode__(self):
        return self.username 
    #上面的__unicode__(self)方法,是当你输出这类对象的时候某人输出,比如这个输出的是这个username这一列.
复制代码

例子(1)输出所有:

def index(request):
    models.UserInfo.objects.create(username='dashuaige')
    models.UserInfo.objects.create(username='luotianshuai')
    print models.UserInfo.objects.all()

输出结果:

[<UserInfo: luotianshuai>, <UserInfo: dashuaige>]

如果使用__unicode__方法默认返回的是对象:[<UserInfo: UserInfo object>]

例子(2)单独查询某一条数据:

def index(request):
    test = models.UserInfo.objects.filter(username='dashuaige')
    print test

输出结果:

[<UserInfo: dashuaige>]

Model扩展知识点(2):添加新的字段报错(添加新的列)

报错信息:

复制代码
localhost:Django_lastday luotim$ python manage.py makemigrations
You are trying to add a non-nullable field 'email' to userinfo without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows)
 2) Quit, and let me add a default in models.py
Select an option: 
复制代码

报错,你准备去添加一个非空的字段的时候原表中已经有的行没有这个字段,就会导致报错!可以通过:允许为空、或者设置默认值来解决

    email = models.EmailField(max_length=64,null=True)
    email2 = models.EmailField(max_length=64,default="luotianshuai@qq.com")

 3、更多参数

复制代码
1、null=True
  数据库中字段是否可以为空
2、blank=True
  django的 Admin 中添加数据时是否可允许空值

#一般null=True & blank=True 咱们搭配着用,出现null=True就用上blank=True
3、primary_key = True
  主键,对AutoField设置主键后,就会代替原来的自增 id 列
4、auto_now 和 auto_now_add
  auto_now   自动创建---无论添加或修改,都是当前操作的时间
  auto_now_add  自动创建---永远是创建时的时间
5、choices  (后台admin下拉菜单)
    USER_TYPE_LIST = (
        (1,u'超级用户'),
        (2,u'普通用户'),
    )
    user_type = models.IntegerField(choices=USER_TYPE_LIST,default=1,verbose_name=u'用户类型')
6、max_length 最大长度
7、default  默认值
8、verbose_name  Admin(后台显示的名称)中字段的显示名称
9、name|db_column  数据库中的字段名称
10、unique=True  不允许重复
11、db_index = True  数据库索引,例如:如果你想通过name查询的更快的话,给他设置为索引即可
12、editable=True  在Admin里是否可编辑
13、error_messages=None  错误提示
14、help_text  在Admin中提示帮助信息
15、validators=[]
16、upload-to
复制代码

最后注释为自己:

Form的作用就是创建标签和获取用户的输入并进行判断,它和Model没有任何关系!通过Form我们可以获取两种状态:正确、错误!

如果是错误的话我们可以获取错误输出,正确的话通过对象.clean()来获取用户输入(字典形式)。Form里的规则主要和用户打交道!

Model就是操作数据库,如果Django没有这个admin后台的话他根本不需要那么多的数据类型,几个常用的即可。Model里的规则主要和Admin打交道。

目的是为了防止用户和admin去肆意操作数据库的两套规则,只是相近的!

上传文件

html文件

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/upload/" method="POST" enctype="multipart/form-data">
        <p><input type="file" name="f1"/></p>
        <p><input type="text" name="hostname"/></p>
        <input type="submit" value="submit"/>
    </form>
</body>
</html>
复制代码

如果你交的是文件的话必须在后面加上:enctype="multipart/form-data"  分片上传

views,在提交文件的时候用:request.FILES 获取用户提交的文件

复制代码
def upload(request):
    if request.method == "POST":
        inp_file = request.FILES
        file_obj1 = inp_file.get('f1')
        print file_obj1.name

        #打开一个文件
        f = open(file_obj1.name,'wb')
        for line in file_obj1.chunks():
            f.write(line)
        f.close()
    return render(request,'home/upload.html')
复制代码

Model连表结构 

  • 一对多:models.ForeignKey(其他表)
  • 多对多:models.ManyToManyField(其他表)
  • 一对一:models.OneToOneField(其他表)

一般创建外键关联的时候,就有一对多,或者多对多    一对一仅在Django中出现。

应用场景:

  • 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
    例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
  • 多对多:在某表中创建一行数据是,有一个可以多选的下拉框
    例如:创建用户信息,需要为用户指定多个爱好
  • 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
    例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据

1、一对多

看下面的表结构:

看用户的类型:都是中文的,如果量少的话还好,如果有10W条数据,那么他占用的空间是不是就非常大了?那如果有另一张表记录着,用户的类型,看下图:

然后在第一张表中,我的用户类型就存储1,2,3就行了。我们给他们做了一个关联,就是SQL中的外键

为什么是1对多呢?在另一张表里比如普通用户,ID=3 为普通用户,在第一张表里有好几个3,意思就是1对多!

Django代码建立一对多:

 

复制代码
class User_Type(models.Model):
    user_type = models.CharField(max_length=64)


class UserInfo(models.Model):
    name = models.CharField(max_length=64)
    age = models.IntegerField(max_length=3)

    #这里列带值用户类型的ID,外键
    usertype = models.ForeignKey(User_Type)
    #这个usertype 对应着 UserInfo里面的数据,他自动关联了
    #在生成数据库的时候他会自动帮你生成一个usertype 
复制代码

2、多对多

场景二:用户和用户组

在Zabbix监控的中,有好几个组比如:运维组、研发组、测试组、DBA组

张三可不可以在多个组里呢?一个组里是不是可以有多个人?当然可以,但是用第一种方法(1对多)可以实现吗?不可以!

如下图:(他只能在写一个组,可以在写一个组,那样太Low了)

事实证明上面的1对多的方式是不可行的,那解决办法是:在原有的两张表在建立一张表来记录关系!

用户表

组表

用户于用户组关系表

代码如下:

复制代码
class UserGroup(models.Model):
    group_name = models.CharField(max_length=64)

class User(models.Model):
    name = models.CharField(max_length=64)
    email = models.CharField(max_length=64)
    mobile = models.CharField(max_length=64)
    user_user_group = models.ManyToManyField('UserGroup')
复制代码

这里不需要我们创建第三张表,Django会为我们自动创建!如下图:

 

3、一对一

一对一不是数据库的一个连表操作,而是Django独有的一个连表操作!如下图

在下面的这种情况下:我们给用户类型设置一个不能重复的参数:unique=True  不允许重复

那么如果在创建一个用户还是用1,2,3就会报错,如果我们在添加一个用户类型,ID=5 BOOS用户,给新建的用户的用户类型ID=5就可以了。

相当于我们伪造出来了一个1对1的连表操作

使用场景:

假设咱们有一个网站,里面有很多用户!现在咱们的用户有人有登录权限有人没有登录权限。我们给这个用户表单独加一个字段,用来记录用户是否有登录权限!

account

代码如下:

复制代码
class UserLogin(models.Model):
    name = models.CharField(max_length=64)
    email = models.CharField(max_length=64)
    mobile = models.CharField(max_length=64)
    user_count = models.OneToOneField('User_Account')

class User_Account(models.Model):
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)
复制代码

效果如下:

Model操作表

1、基本操作

 1.1、增加

复制代码
#
#
# models.Tb1.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs
#
# obj = models.Tb1(c1='xx', c2='oo')  先创建一个对象,然后在save下
# obj.save()
'''
#model
class UserInfo(models.Model):
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)

    def __unicode__(self):
        return self.username

#vies
def index(request):
    models.UserInfo.objects.create(username='luotianshuai',password='dashuaige')
    print models.UserInfo.objects.all()

    obj = HomeForm.ImportForm(request.POST)
    return render(request,'home/index.html',{'obj':obj})


#结果:
[<UserInfo: luotianshuai>]

----------------------------------(**kwargs)-------------------------------------------
我们创建的时候可以通过字典的方式来创建:
dic = {‘username’:'alex','password':'123'}
models.UserInfo.objects.create(**dic) 可以传字典来创建

例子:
获取用户提交的数据来创建用户:

#hmtl
<form action="/useradd/" method="post">
    <p>用户名:{{ obj.username }}</p>
    <p>密码:{{ obj.password }}</p>
    <input type="submit" value="submit"/>
</form>

#views
def useradd(request):
    obj = AccountForm.UserAdd(request.POST)
    if request.method == 'POST':
        if obj.is_valid():
            user_input = obj.clean()
            print user_input
            models.UserInfo.objects.create(**user_input)  #这里直接通过**加上用户的输入即可,因为用户的输入时字典类型的
            print models.UserInfo.objects.all()
            return render(request,'account/useradd.html',{'obj':obj})
    return render(request,'account/useradd.html',{'obj':obj})

#结果
    --用户输入
        {'username': u'dashuaige', 'password': u'123123'}
    --print models.UserInfo.objects.all() 返回值
        [<UserInfo: luotianshuai>, <UserInfo: alex>, <UserInfo: wutenglan>, <UserInfo: dashuaige>]  #注这里我们是通过__unicode__方法进行输出了否则是对象!
'''
复制代码

1.2、查

复制代码
#
#
# models.Tb1.objects.get(id=123)         # 获取单条数据,不存在则报错(不建议使用)
# models.Tb1.objects.all()               # 获取全部
# models.Tb1.objects.filter(name='seven') # 获取指定条件的数据

#models.UserInfo.objects.all().values('password')  #获取指定列的值,可以传多个参数!
[{'password': u'dashuaige'}, {'password': u'123'}, {'password': u'123'}, {'password': u'shuaigenihao'}]

#models.UserInfo.objects.all().values_list('password') #获取指定列的值,可以传多个参数!
[(u'dashuaige',), (u'123',), (u'123',), (u'shuaigenihao',)]

#在前端写choices里,生成select标签就可以通过它来获取
models.UserInfo.objects.all().values_list('id','username')
他输出的是一个元组如下:[(1, u'luotianshuai'), (2, u'alex'), (3, u'wutenglan'), (4, u'dashuaige')]
'''
#实例
#form
------------------------------------------------------------------------------------------------------
from django import forms
from app01 import models
import json


class ImportForm(forms.Form):
    def __init__(self,*arg,**kwargs):
        super(ImportForm,self).__init__(*arg,**kwargs)
        self.fields['admin'].widget.choices = models.UserInfo.objects.all().values_list('id','username')

    admin = forms.IntegerField(
        widget=forms.Select()
    )
------------------------------------------------------------------------------------------------------

#views
------------------------------------------------------------------------------------------------------
def index(request):
    obj = HomeForm.ImportForm(request.POST)
    return render(request,'home/index.html',{'obj':obj})
------------------------------------------------------------------------------------------------------

#html
------------------------------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>可更新下拉菜单</h1>
    <p>{{ obj.admin }}</p>
</body>
</html>
------------------------------------------------------------------------------------------------------

'''
复制代码

效果如下:(Form获取通过Model获取数据生成Select下拉菜单)

 

补充增加(重要):

django的get方法是从数据库的取得一个匹配的结果,返回一个对象,如果记录不存在的话,它会报错。
django的filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。
1.3、删
#
#
# models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据

1.4、改

复制代码
#
# models.Tb1.objects.filter(name='seven').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
#
‘’‘
----------------------------------(**kwargs)-------------------------------------------
更新和添加同理
#views
def useradd(request):
    obj = AccountForm.UserAdd(request.POST)
    if request.method == 'POST':
        if obj.is_valid():
            user_input = obj.clean()
            update_username = user_input['username']
            #先找到用户然后更新他
            models.UserInfo.objects.filter(username=update_username).update(**user_input)
            print models.UserInfo.objects.all()
            return render(request,'account/useradd.html',{'obj':obj})
    return render(request,'account/useradd.html',{'obj':obj})
’‘’


# obj = models.Tb1.objects.get(id=1)  通过创建对象,修改save修改单条数据
# obj.c1 = '111'
# obj.save()                                                 
复制代码

2、进阶操作(了不起的双下划线)

利用双下划线将字段和对应的操作连接起来

复制代码
# 获取个数
#
# models.Tb1.objects.filter(name='seven').count()
# 大于,小于

# models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
# models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

# in
#
# models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

# contains(和数据中like语法相同)
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven")

# range
#
# models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

# 其他类似
#
# startswith,istartswith, endswith, iendswith,  #以什么开始,以什么结束,和上面一样带i的是大小写不敏感的

#排序
# order by
#
# models.Tb1.objects.filter(name='seven').order_by('id')    # asc正序
# models.Tb1.objects.filter(name='seven').order_by('-id')   # desc反序

#分页时使用
# limit 、offset
#
# models.Tb1.objects.all()[10:20]  #取所有数据的10条到20条,分页的时候用的到

#分组使用
# group by
from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.all().values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
复制代码

分组使用例子:

model

class Score(models.Model):
    name = models.CharField(max_length=64)
    scores = models.IntegerField(max_length=3)

    def __unicode__(self):
        return self.name

vies:

from django.db.models import Count,Min,Max,Sum

def index(request):
    print models.Score.objects.all().values('name').annotate(content=Count('scores'))

如果:

输出结果:

[{'content': 2, 'name': u'shuaige'}, {'content': 2, 'name': u'zhangsan'}]

所以:我们可以通过:value(‘name’).annotate(content=Count('scores')) 或者annotate中参数可以为个数、Count、最小值Min、最大值Max、合Sum、来进行输出

3、连表操作(了不起的双下划线)一对多

复制代码
from django.db import models

class UserGroup(models.Model):
    caption = models.CharField(max_length=64)

    def __unicode__(self):
        return self.caption

class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.CharField(max_length=64)
    user_group = models.ForeignKey('UserGroup')

    def __unicode__(self):
        return self.hostname
复制代码

增加UserGroup的时候怎么增加:

    models.UserGroup.objects.create(caption='CEO')
    models.UserGroup.objects.create(caption='DBA')
    models.UserGroup.objects.create(caption='CFO')

增加Host的时候怎么添加:

models.Host.objects.create(hostname='a01.shuaige.com',ip='1.1.1.1',user_group=models.UserGroup.objects.filter(id=1))

我们需要根据对象去找到外键ID对一个的值添加!

实例

html

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/index/" method="post">
        <p>主机名:{{ obj.hostname }}</p>
        <p>主机IP:{{ obj.ip }}</p>
        <p>所属组:{{ obj.group }}</p>
        <input type="submit" value="添加主机"/>
    </form>

</body>
</html>
复制代码

form表单

复制代码
from django import forms
from app01 import models
import json

class ImportForm(forms.Form):
    def __init__(self,*arg,**kwargs):
        super(ImportForm,self).__init__(*arg,**kwargs)
        self.fields['group'].widget.choices = models.UserGroup.objects.all().values_list('id','caption')

    group = forms.CharField(
        widget=forms.Select()
    )
    hostname = forms.CharField()
    ip = forms.GenericIPAddressField()
复制代码

views重要,仔细看里面的解释(回顾时需要注意看里面的注释

复制代码
from django.shortcuts import render
from app01.forms import home as HomeForm
from app01 import models
from django.db.models import Count,Min,Max,Sum

def index(request):
    obj = HomeForm.ImportForm(request.POST)
    if request.method == 'POST':
        if obj.is_valid():
            data = obj.clean()
            '''
            #两种方式
            #第一种方式先获取对象,通过对象的方式添加!
            grop_obj = models.UserGroup.objects.get(id=data['group'])
            print grop_obj
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group=grop_obj)
                                       #这里需要注意group_obj是一个对象原因如下:
            [在Model里咱们的user_group = user_group = models.ForeignKey('UserGroup') 这里他对应了一个对象,所以我们添加的时候添加对象即可,咱们从前端传过来的时候不是一个对象,所以咱们需要先获取一个对象!]
            '''
            print data
            #第二种方式就简单了
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group_id=data['group'])
                                       #因为在存储的时候Django后默认在ForeignKey后面加一个_id所以我们给他加一个_id就可以直接添加了!本质上第一种方式也是通过拼接

        return render(request,'home/index.html',{'obj':obj})
    return render(request,'home/index.html',{'obj':obj})
复制代码

3.2、在实例的基础上我们在做个展示 

views

复制代码
def index(request):
    obj = HomeForm.ImportForm(request.POST)
    host_list = models.Host.objects.all() #获取所有的服务器列表然后展示
    if request.method == 'POST':
        if obj.is_valid():
            data = obj.clean()
            '''
            #两种方式
            #第一种方式先获取对象,通过对象的方式添加!
            grop_obj = models.UserGroup.objects.get(id=data['group'])
            print grop_obj
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group=grop_obj)
                                       #这里需要注意group_obj是一个对象原因如下:
            [在Model里咱们的user_group = user_group = models.ForeignKey('UserGroup') 这里他对应了一个对象,所以我们添加的时候添加对象即可]
            '''
            print data
            #第二种方式就简单了
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group_id=data['group'])
                                       #因为在存储的时候Django后默认在ForeignKey后面加一个_id所以我们给他加一个_id就可以直接添加了


        return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
    
    return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
复制代码

html

复制代码
<body>
    <form action="/index/" method="post">
        <p>主机名:{{ obj.hostname }}</p>
        <p>主机IP:{{ obj.ip }}</p>
        <p>所属组:{{ obj.group }}</p>
        <input type="submit" value="添加主机"/>
    </form>
    <table>
        {% for item in host_list %}
            <tr>
            <td>{{ item.hostname }}</td>
       {#这里存储的是ID,因为user_group是一个对象(一行数据),我们可以根据对象.caption来取出他对一个的中文#}
            <td>{{ item.user_group.caption }}</td>
            </tr>
        {% endfor %}
    </table>
</body>
复制代码

效果:

 

3.3、在上面的基础上修改为查询的时候:

request只支持两种方式POST或者GET

仅修改views即可

复制代码
def index(request):
    obj = HomeForm.ImportForm(request.POST)
    val = request.GET.get('ip') #请求如果为IP的时候
    host_list = models.Host.objects.filter(ip=val)
    if request.method == 'POST':
        if obj.is_valid():
            data = obj.clean()
            '''
            #两种方式
            #第一种方式先获取对象,通过对象的方式添加!
            grop_obj = models.UserGroup.objects.get(id=data['group'])
            print grop_obj
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group=grop_obj)
                                       #这里需要注意group_obj是一个对象原因如下:
            [在Model里咱们的user_group = user_group = models.ForeignKey('UserGroup') 这里他对应了一个对象,所以我们添加的时候添加对象即可]
            '''
            print data
            #第二种方式就简单了
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group_id=data['group'])
                                       #因为在存储的时候Django后默认在ForeignKey后面加一个_id所以我们给他加一个_id就可以直接添加了


        return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
    return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
复制代码

测试:

结果:

那我根据组怎么查询?

这里需要注意在拿数据的时候是用."点",但是在查询的时候需要用了不起的双下划线!

    val = request.GET.get('usergroup')
    host_list = models.Host.objects.filter(user_group__caption=val)

views

复制代码
def index(request):
    obj = HomeForm.ImportForm(request.POST)
    val = request.GET.get('usergroup')
    host_list = models.Host.objects.filter(user_group__caption=val)
    if request.method == 'POST':
        if obj.is_valid():
            data = obj.clean()
            '''
            #两种方式
            #第一种方式先获取对象,通过对象的方式添加!
            grop_obj = models.UserGroup.objects.get(id=data['group'])
            print grop_obj
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group=grop_obj)
                                       #这里需要注意group_obj是一个对象原因如下:
            [在Model里咱们的user_group = user_group = models.ForeignKey('UserGroup') 这里他对应了一个对象,所以我们添加的时候添加对象即可]
            '''
            print data
            #第二种方式就简单了
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group_id=data['group'])
                                       #因为在存储的时候Django后默认在ForeignKey后面加一个_id所以我们给他加一个_id就可以直接添加了


        return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
    return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
复制代码

查询测试:

总结:在添加的时候通过_id来建立关系、获取数据的时候通过.、如果在filter里面跨表查询的时候就得用两个下划线"__"

双下划线玩:

Model,多关联!

复制代码
#/usr/bin/env python
#-*- coding:utf-8 -*-

from __future__ import unicode_literals
# Create your models here.

from django.db import models

class UserType(models.Model):
    typelist = models.CharField(max_length=64,null=True,blank=True)

class UserGroup(models.Model):
    caption = models.CharField(max_length=64)
    user_type = models.ForeignKey('UserType')

    def __unicode__(self):
        return self.caption

class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.CharField(max_length=64)
    user_group = models.ForeignKey('UserGroup')

    def __unicode__(self):
        return self.hostname
复制代码

添加数据

复制代码
def useradd(request):
    #添加用户类型
    models.UserType.objects.create(typelist='超级用户')
    models.UserType.objects.create(typelist='金牌用户')
    models.UserType.objects.create(typelist='普通用户')
    #添加用户组
    models.UserGroup.objects.create(caption='CEO',user_type_id=1)
    models.UserGroup.objects.create(caption='CFO',user_type_id=2)
    models.UserGroup.objects.create(caption='CTO',user_type_id=3)
    #添加主机
    models.Host.objects.create(hostname='a01.shuaige.com',ip='1.1.1.1',user_group_id=1)
    models.Host.objects.create(hostname='a02.shuaige.com',ip='2.2.2.2',user_group_id=2)
    models.Host.objects.create(hostname='a03.shuaige.com',ip='3.3.3.3',user_group_id=3)
    return HttpResponse('OK')
复制代码

Views里设置查询规则:__  哈哈,双下划线如果做了多个关联就可以是用多个__下划线去查询到值

复制代码
def index(request):
    obj = HomeForm.ImportForm(request.POST)
    val = request.GET.get('usertype')
    host_list = models.Host.objects.filter(user_group__user_type__typelist=val)
    if request.method == 'POST':
        if obj.is_valid():
            data = obj.clean()
            '''
            #两种方式
            #第一种方式先获取对象,通过对象的方式添加!
            grop_obj = models.UserGroup.objects.get(id=data['group'])
            print grop_obj
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group=grop_obj)
                                       #这里需要注意group_obj是一个对象原因如下:
            [在Model里咱们的user_group = user_group = models.ForeignKey('UserGroup') 这里他对应了一个对象,所以我们添加的时候添加对象即可]
            '''
            print data
            #第二种方式就简单了
            models.Host.objects.create(hostname=data['hostname'],
                                       ip=data['ip'],
                                       user_group_id=data['group'])
                                       #因为在存储的时候Django后默认在ForeignKey后面加一个_id所以我们给他加一个_id就可以直接添加了


        return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
    return render(request,'home/index.html',{'obj':obj,'host_list':host_list})
复制代码

效果:

 

posted @ 2017-11-28 18:05  北方客888  阅读(312)  评论(0编辑  收藏  举报