一 Model连表关系

一对多:models.ForeignKey(其他表)
    多对多:models.ManyToManyField(其他表)
    一对一:models.OneToOneField(其他表)
    一般在数据库中创建外键关联时,就有一对多或者多对多,而一对一是Django独有的。

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

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,那么用户类型就是金牌会员,不能选择)

      

一对多小结:
1、外键user_group在数据库中是以新字段名称user_group_id(Django自动生成的名称:外键名+'_id')存储的,
   这样将前端的数据存入数据库时,就可以构造字典参数,直接把字典作为参数传给数据库创建函数create;
2、在实例中,外键user_group为对象,对应着UserGroup中的一行数据;
3、数据库中获取数据采用"."操作符;
4、数据库中跨表查询关联外键的字段值采用"__"双下划线操作符(注意双下划线操作符),比如实例中的
   user_list = models.User.objects.filter(user_group__caption=val),它表示查询关联外键user_group
   中caption字段值为val的User表中的相关用户数据

5、在外键的多级关联中,在数据库中查询数据时,下划线可以连着使用

参考资料:

http://www.cnblogs.com/wupeiqi/articles/5246483.html

http://www.cnblogs.com/luotianshuai/p/5301343.html

Django基础——Model篇(二)的更多相关文章

  1. Django基础——Model篇(三)

    一 Django ORM中的概念 ORM —— 关系对象映射,是Object Relational Mapping的简写,是用来简化数据库操作的框架 Django ORM遵循Code Frist原则, ...

  2. Django基础——Model篇(一)

    到目前为止,当程序涉及到数据库相关操作时,我们一般都会这么操作:    (1)创建数据库,设计表结构和字段    (2)使用MySQLdb来连接数据库,并编写数据访问层代码    (3)业务逻辑层去调 ...

  3. LeetCode刷题191130 --基础知识篇 二叉搜索树

    休息了两天,状态恢复了一下,补充点基础知识. 二叉搜索树 搜索树数据结构支持许多动态集合操作,包括Search,minimum,maximum,predecessor(前驱),successor(后继 ...

  4. Django进阶Model篇—数据库操作(ORM)

    一.数据库配置 django 默认支持sqlite.mysql.oracle.postgresql数据库,像db2和sqlserver之类的数据库需要第三方的支持,具体详见https://docs.d ...

  5. Django进阶Model篇008 - 使用原生sql

    注意:使用原生sql的方式主要目的是解决一些很复杂的sql不能用ORM的方式写出的问题. 一.extra:结果集修改器-一种提供额外查询参数的机制 二.执行原始sql并返回模型实例 三.直接执行自定义 ...

  6. Django进阶Model篇004 - ORM常用操作

    一.增加 create和save方法 实例: 1.增加一条作者记录 >>> from hello.models import * >>> Author.object ...

  7. Django进阶Model篇002 - 模型类的定义

    一.创建数据模型. 实例: 作者模型:一个作者有姓名. 作者详情模型:把作者的详情放到详情表,包含性别.email 地址和出生日期,作者详情模型与作者模型之间是一对一的关系(OneToOneField ...

  8. django 基础框架学习 (二)

    Django框架基础-02 Django缓存cookie 1.说明        当我们服务器在响应数据的同时,希望写⼊⼀些缓存数据到客户端        我们可以选择在响应的同时,将要写⼊到客户端的 ...

  9. Django基础第一篇

    目录 1.Django MTV框架简介 2.基础命令创建项目的配置说明 3.前后端交互案例 4.基于数据库实现数据交互增删改查 Django简介 Django框架的设计模式借鉴了MVC的思想,和MVC ...

随机推荐

  1. Tornado sqlalchemy

    上篇文章提到了,最近在用 Python 做一个网站.除了 Tornado ,主要还用到了 SQLAlchemy.这篇就是介绍我在使用 SQLAlchemy 的过程中,学到的一些知识. 首先说下,由于最 ...

  2. 技术英文单词贴--G

    G generator 发电机,发生器,生产者

  3. Asp.net web api部署在某些服务器上老是404

    asp.net web api部署在Windows服务器上后,按照WebAPI定义的路由访问,老是出现404,但定义一个静态文件从站点访问,却又OK. 这时,便可以确定是WebAPI路由出了问题,经调 ...

  4. ArcGIS删除部分数据后全图范围不正确

      我有一个全国地图的图层,现在删除图层中其他省份,只保留山东省的图形,但是点击全图后,全图范围仍然是全国地图时候的全图范围,使用的版本是ArcGIS9.3,数据存放在9.3的个人数据库中(Perso ...

  5. js关闭浏览器的tab页(兼容)

    由于在脚本中使用了 window.close(), 当前非弹出窗口在最新版本的chrome和firefox里总是不能关闭,而在 IE中是可以关闭的 . 在console中弹出提示"Scrip ...

  6. php 递归函数的三种实现方式

    递归函数是我们常用到的一类函数,最基本的特点是函数自身调用自身,但必须在调用自身前有条件判断,否则无限无限调用下去.实现递归函数可以采取什么方式呢?本文列出了三种基本方式.理解其原来需要一定的基础知识 ...

  7. [Delphi编译错误]F2084 Internal Error: U2107

    看到这个错误真是头痛,这是一个很旧的项目了,想修改下东西,清理下工程一编译出现这个该死的错误,百度了下,也没解决问题.没办法只好编译所有的BPL了.   这个项目是带包编译的,而且带了几个自己的包. ...

  8. c#开发Mongo笔记第三篇

    今天主要测试了一下查询功能了,当然了主要还是为了让查询可以和我们平时使用的实体对象关联起来,并且 那些BsonDocument和Collection我们操作起来不是太方便的 还是首先定义了一个用户类, ...

  9. PowerDesigner 建模

    File—New Model—Physical Data Model—Physical Diagram—Model name设置为test,DBMS属性设置为Microsoft SQL Server  ...

  10. linux 管道命令 竖线 ‘ | ’

    管道符号,是unix功能强大的一个地方,符号是一条竖线:"|", 用法: command 1 | command 2 他的功能是把第一个命令command 1执行的结果作为comm ...