一 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              

 

posted on 2016-03-30 17:55  人生苦短,python当歌  阅读(737)  评论(0编辑  收藏  举报