Python之路【第十七篇】Django进阶篇
规范
确立规范的好处:
- 代码可读性高
- 方便代码的定位极其查找
- 为以后代码扩容带来便利
场景:
在多个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='', 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='', 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''}, {'password': u''}, {'password': u'shuaigenihao'}]
- #models.UserInfo.objects.all().values_list('password') #获取指定列的值,可以传多个参数!
- [(u'dashuaige',), (u'',), (u'',), (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})
效果:
Python之路【第十七篇】Django进阶篇的更多相关文章
- 02:Django进阶篇
目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...
- Python学习-day20 django进阶篇
Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行 ...
- Django进阶篇【1】
注:本篇是Django进阶篇章,适合人群:有Django基础,关于Django基础篇,将在下一章节中补充! 首先我们一起了解下Django整个请求生命周期: Django 请求流程,生命周期: 路由部 ...
- django进阶篇
原文连接:http://www.cnblogs.com/wupeiqi/articles/5246483.html Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创 ...
- Django进阶篇(一)
Form django中的Form一般有两种功能: 1.输入html 2.验证用户输入 最简易的form验证: <!DOCTYPE html> <html lang="en ...
- Python之路【第十六篇续】Django进阶篇
Django请求生命周期 首先:对于所有的web框架来说本质就是一个socket服务端,浏览器是socket客户端 路由系统 在Django的urls中我们可以根据一个URL对应一个函数名来定义路由规 ...
- Python之路(第二十七篇) 面向对象进阶:内置方法、描述符
一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()( ...
- python Django 进阶篇
Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. ...
- Python之路(第十七篇)logging模块
一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变 ...
随机推荐
- linux中tar之解压和压缩常用
我们知道在windows中解压和压缩有两个非常强大的工具winRar和国产的好压工具,在linux中也有一款强大的解压和压缩工具.那就是大名鼎鼎的tar.我们首先看看tar命令的使用格式 语法:tar ...
- java设计模式之模板方法模式
模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.通俗的说的就是有很多相同的步骤的,在某一些地方可能有一些差 ...
- POJ1962Corporative Network[带权并查集]
Corporative Network Time Limit: 3000MS Memory Limit: 30000K Total Submissions: 3945 Accepted: 14 ...
- Dialog详解(包括进度条、PopupWindow、自定义view、自定义样式的对话框)
Dialog详解(包括进度条.PopupWindow.自定义view.自定义样式的对话框) Android中提供了多种对话框,在实际应用中我们可能会需要修改这些已有的对话框.本实例就是从实际出发, ...
- json jsonp的区别
前言: 说到AJAX就会不可避免的面临两个问题,第一个是AJAX以何种格式来交换数据?第二个是跨域的需求如何解决?这两个问题目前都有不同的解决方案,比如数据可以用自定义字符串或者用XML来描述,跨域 ...
- java并发编程学习:用 Semaphore (信号量)控制并发资源
并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知 ...
- ionic angularJS input 相关指令 以及定时器 的使用
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" conte ...
- Windows phone应用开发[20]-禁止Pivot手势
相比Ios 和Android. Windows Phone 控件库中多了两个比较特殊的空间Pivot 枢轴和Panamera 全景视图控件.在基于枢轴控件Pivot中我们经常会碰到一些比较特殊应用场景 ...
- itertools
0. Python中引入itertools 1. 笛卡尔积: product(iter1, iter2,...,iterN,[repeat=i]) from itertools import prod ...
- [转] Struts2入门示例教程
原文地址:http://blog.csdn.net/wwwgeyang777/article/details/19078545/ 回顾Struts2的使用过程,网上搜的教程多多少少都会有点问题,重新记 ...