一 Model连表关系
一对多:models.ForeignKey(其他表) 应用场景: |
1 一对多
一对多表结构是为了解决空间浪费问题,通过一张数据库表去关联另外一张数据库表来解决重复出现的字段,这样在这张表中只会存储相应的ID来代替相应的字段。
为什么是1对多呢?因为在第2张表里,金牌会员的ID=3,而在第3张表里有好2个3,即表示1对多。
在Model中生成一对多表结构(models.py文件)
例1:第1种一对多(默认关联id)
# 用户类型类
class UserType(models.Model):
user_type = models.CharField(max_length=64)
#用户信息类
class UserInfo(models.Model):
username = models.CharField(max_length=64)
email = models.CharField(max_length=32)
# 外键关联 usertype对应着UserInfo表中的1列数据,他自动关联了UserType类
usertype = models.ForeignKey(UserType)
例2:第2种一对多(指定关联字段)
# 业务线类
class Business(models.Model):
#设置为主键
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
# 主机类,表示主机属于哪一条业务线
class Host(models.Model):
hostname = models.CharField(max_length=16)
# 括号中的参数可加引号,也可以不加引号,效果一样,to_field参数表示关联哪个字段
business = models.ForeignKey(Business,to_field='nid')
例3:第3种一对多(多级外键关联)
#一对多 多级外键关联
class A(models.Model):
name = models.CharField(max_length=16)
def __unicode__(self):
return self.name
class UserGroup(models.Model):
caption = models.CharField(max_length=32)
obj_a = models.ForeignKey(A)
def __unicode__(self):
return self.caption
class User(models.Model):
username = models.CharField(max_length=32)
user_group = models.ForeignKey(UserGroup)
def __unicode__(self):
return self.hostname
2 多对多
在Zabbix监控中会存在用户与用户组的概念,在这里同一个人可以属于多个用户组,同时同一个用户组也可以包括多个人。在这个场景中一对多的表关系结构是不能解决这个问题的,从而引出了多对多的概念。
为什么是多对多呢?因为在对于CEO组,有两个人,即1个组包括了多个人;同时对于alex,他分别属于两个不同组,那么认为这种关系就是多对多。
在Model中生成多对多表结构(models.py文件)
# model中的多对多
# 应用场景:用户与用户组(一个用户可以在多个组,同时一个组可以同时存在多个用户)
class UserGroup(models.Model):
group_name = models.CharField(max_length=16)
class User(models.Model):
name = models.CharField(max_length=16)
email = models.CharField(max_length=16)
mobile = models.CharField(max_length=16)
# 两张表建立多对多,django默认会生成一张数据库表来建立两者的关系
relationship = models.ManyToManyField(UserGroup)
注:对于多对多,在models.py中我们只写了两个类,即生成两张数据库表,但两张表明显不能很好的表示多对多的关系,在这种情况下Django默认为我们生成另外一张表来建立这种关系,即用3张表来表示用户和用户组之间的关系。
3 一对一
一对一不是数据库的连表结构,而是Django独有的,他是在一对多的基础伪造出来的。即在一对多中限定某个指定的字段不能重复,一旦重复数据库报错,这样就相当于构造了一个一对一的连表关系。在上述一对多中,用户级别是可重复的,但在一对一关系中,一个用户级别只能对应一个用户,不允许重复。
应用场景:假设在一个公司的所有用户中,只有alex和eric可以登录后台管理,那么这个场景就可以使用一对一的连表关系。
在Model中生成一对一表结构(models.py文件)
# model中的一对一,相当于对一对多进行限制后产生的,由Django完成,且是Django独有的
# 应用场景:在很多用户中只有某些用户能够登录,这时id不能重复,即同一个账号ID不能出现多次
# 下表为所有的用户信息表
class User(models.Model):
name = models.CharField(max_length=16)
email = models.CharField(max_length=16)
mobile = models.CharField(max_length=16)
# 下表为能够登录的用户的用户名和密码
class admim(models.Model):
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
# django自动完成关系的建立
user_info = models.OneToOneField(User)
二 Model连表操作(了不起的双下划线)
1 一对多连表操作
实例1:在页面创建用户,同时在页面进行展示
models.py文件
#-*- coding:utf-8 -*-
from __future__ import unicode_literals
from django.db import models
#一对多连表关系
#用户组表
class UserGroup(models.Model):
caption = models.CharField(max_length=32)
def __unicode__(self):
return self.caption
#用户表
class User(models.Model):
username = models.CharField(max_length=32)
#user_group为对象,对应UserGroup中的一行数据
user_group = models.ForeignKey(UserGroup)
def __unicode__(self):
return self.hostname
forms模块中的foreign.py文件
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django import forms
from app01 import models
class UserForm(forms.Form):
# 创建用户名
username = forms.CharField()
# 创建用户组的两种方法
# 第1种方法:
# group = forms.IntegerField(widget=forms.Select())
# 第2种方法:用户组字段名=models.py中User类的用户组字段名+"_id"
# 因为外键user_group字段在User数据库表中字段名默认变成了"user_group_id",这样做的好处:username和user_group_id直接组成字典
# 传给Model,因为Model创建数据时是接受字典形式参数的,就是views/foreign.py中的all_data
user_group_id = forms.IntegerField(widget=forms.Select())
def __init__(self,*args,**kwargs):
# 执行父类的构造方法
super(UserForm,self).__init__(*args,**kwargs)
# 配合创建用户组的第1种方法使用,这里同时注意values_list方法的使用
# self.fields['group'].widget.choices = models.UserGroup.objects.all().values_list('id','caption')
# 配合创建用户组的第2种方法使用
self.fields['user_group_id'].widget.choices = models.UserGroup.objects.all().values_list('id','caption')
views模块中foreign.py文件
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django.shortcuts import HttpResponse,render
from app01 import models
from app01.forms import foreign as ForeignForm
# 创建用户组表
def create_user_group(request):
# 创建用户组的类型,运行一次后注释,防止多次生成
# models.UserGroup.objects.create(caption='CEO')
# models.UserGroup.objects.create(caption='CTO')
# models.UserGroup.objects.create(caption='COO')
return HttpResponse('ok')
# 创建用户表
def create_user(request):
# 将请求数据作为参数传递给UserForm,生成相应对象
obj = ForeignForm.UserForm(request.POST)
# 判断请求类型是否为'POST'类型
if request.method == 'POST':
# 进行From表单验证,验证其有效性
if obj.is_valid():
# 获取请求数据
all_data = obj.clean()
# 把获取的数据存储到数据库,有3种方法
# 第1种方法(对象级别操作):先获取对象
# group_obj = models.UserGroup.objects.get(id=all_data['group'])
# 为什么创建数据库表时,第二个参数(关联外键)user_group=group_obj?
# 这是因为在Model里user_group = models.ForeignKey('UserGroup'),这里user_group实际上是关联了一个对象,
# 所以在传递参数时要传递相应的对象参数。
# models.User.objects.create(username=all_data['username'],user_group=group_obj)
# 第2种方法(数据库级别操作,这里注意Django默认给外键字段名加上了"_id")
# models.User.objects.create(username=all_data['username'],user_group_id=all_data['group'])
# 第3种方法(传字典参数,但是要注意form中已经组成了字典数据)
models.User.objects.create(**all_data)
# 打印数据库中所以信息的条数
print models.User.objects.all().count()
else:
# 写上报错提示信息,form表单的验证信息,这里省略了
pass
# 获取所有的用户信息列表
user_list = models.User.objects.all()
return render(request,'foreign/create_user.html',{'obj':obj,'user_list':user_list})
templates/foreign目录下的create_user.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/create_user/" method="post">
<p>用户名:{{ obj.username }}</p>
{#<p>用户组:{{ obj.user_group }}</p>#}
<p>用户组:{{ obj.user_group_id }}</p>
<input type="submit" value="submit"/>
</form>
<table>
{% for item in user_list %}
<tr>
<td>{{ item.username }}</td>
{#注意此处的操作,user_group是1个对象(表示数据库中一行数据),而用户组在数据库中是按ID存储的,所以要转换成中文,注意模板中的.号操作#}
<td>{{ item.user_group.caption }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
运行效果如下图所示:
实例2:在页面查询用户,同时在页面展示该用户的相关信息
在实例1的基础上,修改views模块中foreign.py文件即可
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django.shortcuts import HttpResponse,render
from app01 import models
from app01.forms import foreign as ForeignForm
# 创建用户组表
def create_user_group(request):
# 创建用户组的类型,运行一次后注释,防止多次生成
# models.UserGroup.objects.create(caption='CEO')
# models.UserGroup.objects.create(caption='CTO')
# models.UserGroup.objects.create(caption='COO')
return HttpResponse('ok')
# 创建用户表
def create_user(request):
# 将请求数据作为参数传递给UserForm,生成相应对象
obj = ForeignForm.UserForm(request.POST)
# 判断请求类型是否为'POST'类型
if request.method == 'POST':
# 进行From表单验证,验证其有效性
if obj.is_valid():
# 获取请求数据
all_data = obj.clean()
# 把获取的数据存储到数据库,有3种方法
# 第1种方法(对象级别操作):先获取对象
# group_obj = models.UserGroup.objects.get(id=all_data['group'])
# 为什么创建数据库表时,第二个参数(关联外键)user_group=group_obj?
# 这是因为在Model里user_group = models.ForeignKey('UserGroup'),这里user_group实际上是关联了一个对象,
# 所以在传递参数时要传递相应的对象参数。
# models.User.objects.create(username=all_data['username'],user_group=group_obj)
# 第2种方法(数据库级别操作,这里注意Django默认给外键字段名加上了"_id")
# models.User.objects.create(username=all_data['username'],user_group_id=all_data['group'])
# 第3种方法(传字典参数,但是要注意form中已经组成了字典数据)
models.User.objects.create(**all_data)
# 打印数据库中所以信息的条数
print models.User.objects.all().count()
else:
# 写上报错提示信息,form表单的验证信息,这里省略了
pass
# 根据输入的用户名,查找该用户所在的组
# 例如URL中输入http://127.0.0.1:8000/create_user/?user=alex,表示获取数据库中username='alex'的相关数据
# 获取get请求中user的值,例如user='alex',那么下面的val='alex'
val = request.GET.get('user')
# 获取数据库中对应字段username='val'的一行数据
user_list = models.User.objects.filter(username=val)
return render(request,'foreign/create_user.html',{'obj':obj,'user_list':user_list})
运行效果如下图所示:
实例3:在页面查询用户组,同时在页面展示该用户组的用户
在实例1的基础上,修改views模块中foreign.py文件即可
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django.shortcuts import HttpResponse,render
from app01 import models
from app01.forms import foreign as ForeignForm
# 创建用户组表
def create_user_group(request):
# 创建用户组的类型,运行一次后注释,防止多次生成
# models.UserGroup.objects.create(caption='CEO')
# models.UserGroup.objects.create(caption='CTO')
# models.UserGroup.objects.create(caption='COO')
return HttpResponse('ok')
# 创建用户表
def create_user(request):
# 将请求数据作为参数传递给UserForm,生成相应对象
obj = ForeignForm.UserForm(request.POST)
# 判断请求类型是否为'POST'类型
if request.method == 'POST':
# 进行From表单验证,验证其有效性
if obj.is_valid():
# 获取请求数据
all_data = obj.clean()
# 把获取的数据存储到数据库,有3种方法
# 第1种方法(对象级别操作):先获取对象
# group_obj = models.UserGroup.objects.get(id=all_data['group'])
# 为什么创建数据库表时,第二个参数(关联外键)user_group=group_obj?
# 这是因为在Model里user_group = models.ForeignKey('UserGroup'),这里user_group实际上是关联了一个对象,
# 所以在传递参数时要传递相应的对象参数。
# models.User.objects.create(username=all_data['username'],user_group=group_obj)
# 第2种方法(数据库级别操作,这里注意Django默认给外键字段名加上了"_id")
# models.User.objects.create(username=all_data['username'],user_group_id=all_data['group'])
# 第3种方法(传字典参数,但是要注意form中已经组成了字典数据)
models.User.objects.create(**all_data)
# 打印数据库中所以信息的条数
print models.User.objects.all().count()
else:
# 写上报错提示信息,form表单的验证信息,这里省略了
pass
# 根据输入的用户组,查找该组的所有用户信息
# http://127.0.0.1:8000/create_user/?usergroup=CEO
# 获取输入的用户组信息
val = request.GET.get('usergroup')
# 跨表查询,双下划线
user_list = models.User.objects.filter(user_group__caption=val)
return render(request,'foreign/create_user.html',{'obj':obj,'user_list':user_list})
运行效果如下图所示:
实例4:一对多连表关系中的多级关联
models.py文件
#/usr/bin/env python
#-*- coding:utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
#一对多连表关系中的多级关联
#用户类型表
class UserType(models.Model):
type_list = models.CharField(max_length=64,null=True,blank=True)
def __unicode__(self):
return self.type_list
# 用户组表
class UserGroup(models.Model):
caption = models.CharField(max_length=32)
user_type = models.ForeignKey('UserType')
def __unicode__(self):
return self.caption
# 用户表
class User(models.Model):
username = models.CharField(max_length=32)
user_group = models.ForeignKey('UserGroup')
def __unicode__(self):
return self.username
views模块中foreign.py文件
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django.shortcuts import HttpResponse,render
from app01 import models
from app01.forms import foreign as ForeignForm
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django.shortcuts import HttpResponse,render
from app01 import models
from app01.forms import foreign as ForeignForm
def create_user_group(request):
#添加用户类型
models.UserType.objects.create(type_list='金牌会员')
models.UserType.objects.create(type_list='银牌会员')
models.UserType.objects.create(type_list='普通会员')
#添加用户组
models.UserGroup.objects.create(caption='CEO',user_type_id=1)
models.UserGroup.objects.create(caption='CTO',user_type_id=2)
models.UserGroup.objects.create(caption='COO',user_type_id=3)
return HttpResponse('OK')
def create_user(request):
obj = ForeignForm.UserForm(request.POST)
if request.method == 'POST':
if obj.is_valid():
all_data = obj.clean()
#先获取对象,然后把对象作为参数
group_obj = models.UserGroup.objects.get(id=all_data['user_group'])
models.User.objects.create(username=all_data['username'],user_group=group_obj)
# 测试1:获取所有的用户信息表
# user_list = models.User.objects.all()
#测试2:从URL中获取请求的信息
val = request.GET.get('usertype')
# 注意跨表多级查询外键时,双下划线的使用
user_list = models.User.objects.filter(user_group__user_type__type_list=val)
return render(request,'foreign/create_user.html',{'obj':obj,'user_list':user_list})
forms模块中foreign.py文件
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'mcp'
from django import forms
from app01 import models
class UserForm(forms.Form):
username = forms.CharField()
user_group = forms.IntegerField(widget=forms.Select())
user_type = forms.IntegerField(widget=forms.Select())
def __init__(self,*args,**kwargs):
super(UserForm,self).__init__(*args,**kwargs)
self.fields['user_group'].widget.choices = models.UserGroup.objects.all().values_list('id','caption')
self.fields['user_type'].widget.choices = models.UserType.objects.all().values_list('id','type_list')
templates/foreign目录下的create_user.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/create_user/" method="post">
<p>用户名:{{ obj.username }}</p>
<p>用户组:{{ obj.user_group }}</p>
<p>用户类型{{ obj.user_type }}</p>
<input type="submit" value="submit"/>
</form>
<table>
{% for item in user_list %}
<tr>
<td>{{ item.username }}</td>
<td>{{ item.user_group.caption }}</td>
<td>{{ item.user_group.user_type.type_list }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
测试1和测试2运行结果分别如下图所示:(在实例4的多级关联中,实际上用户组合用户类型是已经绑定死了的,不能选择,例如加入用户组是CEO,那么用户类型就是金牌会员,不能选择)
一对多小结: 5、在外键的多级关联中,在数据库中查询数据时,下划线可以连着使用 |
参考资料:
http://www.cnblogs.com/wupeiqi/articles/5246483.html
http://www.cnblogs.com/luotianshuai/p/5301343.html