• Django(四) ORM 外键操作及初识Ajax


    一、内容回顾

    1、Django请求的生命周期:

    ​ 路由系统 -> 视图函数(获取模板+数据 -> 渲染) -> 字符串返回给用户

    2、路由系统:

    /index/                #->  函数或类.as_view()
    /detail/(d+)          #->  函数(参数) 或 类.as_view()(参数)
    /detail/(?P<nid>d+)   #->  函数(参数) 或 类.as_view()(参数)
    /detail/               #->  include("app01.urls")
    /detail/    name='a1'  #->  include("app01.urls")
                           #->  视图中:reverse
                           #->  模板中:{% url "a1" %}

    3、视图:

    FBV:函数

    def index(request,*args,**kwargs):

    CBV:类

    class Home(views.View):
        def get(self,reqeust,*args,**kwargs):

    获取用户请求中的数据:

    request.POST.get
    request.GET.get
    reqeust.FILES.get()
    
    
    # checkbox,select
    
    ........getlist()

    文件对象

    文件对象 = reqeust.FILES.get()
    文件对象.name
    文件对象.size
    文件对象.chunks()  # 直接循环文件对象也可以
    
    # 特殊的设置 <form action="/login/" method="POST" enctype="multipart/form-data">

    给用户返回数据:

    render(request, "模板文件路径", {'k1': [1,2,3,4],"k2": {'name': 'lgeng','age': 73}})
    redirect("URL")   
    HttpResponse('字符串')   

    4、模板语言

    render(request, "模板路径", {'obj': 1234, 'k1': [1,2,3,4],"k2": {'name': 'lgeng','age': 22}})
    <html>
    <body>
    <h1> {{ obj }} </h1>      # 取变量值
    <h1> {{ k1.3 }} </h1>     # 取列表的值
    <h1> {{ k2.name }} </h1>  # 取字典的值
    
    {% for i in k1 %}       # 循环列表的值
    <p> {{ i }} </p>          # 取值
    {% endfor %}{% for row in k2.keys %}  # 循环取字典key
    {{ row }}        # 取值
    {% endfor %}{% for row in k2.values %} # 循环取字典values
    {{ row }}        # 取值
    {% endfor %}{% for k,v in k2.items %}  # 循环取key、values
    {{ k }} - {{v}}  # 取值
    {% endfor %}
    # 注意:模板语言循环keys、values、items这里不加 "()"
    </body>
    </html>

    5、ORM

    • a. 创建类和字段
    class User(models.Model):
        age = models.IntergerFiled()            # 整数在后面加长度不起作用
        name = models.CharField(max_length=10)  # 字符长度
    
    # 生成数据库结构命令
    Python manage.py makemigrations
    python manage.py migrate
    
    # settings.py 配置 注册APP
    • b. 操作
    #
    models.User.objects.create(name='qianxiaohu',age=18) # 第一种
    dic = {'name': 'xx', 'age': 19}                      # 第二种
    models.User.objects.create(**dic)    # **dic
    obj = models.User(name='xiao',age=18)                # 第三种
    obj.save()
    #
    models.User.objects.filter(id=1).delete()
    #
    models.User.objects.filter(id__gt=1).update(name='alex',age=84)
    dic = {'name': 'xx', 'age': 19}
    models.User.objects.filter(id__gt=1).update(**dic)
    #
    models.User.objects.filter(id=1,name='root')
    models.User.objects.filter(id__gt=1,name='root')# 大于
    models.User.objects.filter(id__lt=1)            # 小于
    models.User.objects.filter(id__gte=1)           # 大于等于
    models.User.objects.filter(id__lte=1)           # 小于等于
    
    models.User.objects.filter(id=1,name='root')
    dic = {'name': 'xx', 'age__gt': 19}
    models.User.objects.filter(**dic)  # 这里也可以传字典,增删改查都可以加字典

    二、Django ORM 外键操作一对多

    a.1、创建一对多表结构

    class Business(models.Model):
        # id
        caption = models.CharField(max_length=32)
        code = models.CharField(max_length=32,null=True,default="SA")
        # 新增列 允许为空null=True 或 default="SA",就不会提示信息
    
    class Host(models.Model):
        nid = models.AutoField(primary_key=True)
        hostname = models.CharField(max_length=32,db_index=True)  # 加上索引
        ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
        port = models.IntegerField()
        b = models.ForeignKey(to="Business", to_field='id')  # 表中生成列名:b_id
        # b = models.ForeignKey("Business", to_field='id')   # 外键约束,
        # b = models.ForeignKey("Business")   #只写表名,默认与那张表主键关联
    
    # 外键:
    #   v = models.Host.objects.filter(nid__gt=0)
    #   v[0].b.caption  #---->  通过.进行跨表

    a.2 获取单表数据的三种方式

    v1 = models.Business.objects.all() #QuerySet类型 ,内部元素都是对象(对象内类似字典结构)
    v2 = models.Business.objects.all().values('id','caption') #QuerySet ,内部元素都是字典 
    v3 = models.Business.objects.all().values_list('id','caption') #QuerySet ,内部元素都是元组
    #===============
    models.Business.objects.get(id=1) #获取到的一个对象,如果不存在就报错
    obj = models.Business.objects.filter(id=1).first() #对象或者None

    a.3、示例:展示业务线

    urls.py

    from app01 import views
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^business$', views.business),
    ]

    views.py

    from django.shortcuts import render, redirect, HttpResponse
    from app01 import models
    
    def business(request):
        v1 = models.Business.objects.all()
        # QuerySet
        # [obj(id,caption,code), obj(id,caption,code), obj(id,caption,code)]
    
        v2 = models.Business.objects.values('id','caption')
        # QuerySet
        # [{'id':1,'caption':'运维'},{'id':1,'caption':'运维'},{'id':1,'caption':'运维'}]
    
        v3 = models.Business.objects.values_list('id','caption')
        # QuerySet
        # [(1,运维),(2,开发)]
    
        return render(request, "business.html", {'v1':v1,'v2':v2 ,'v3':v3})

    business.html

    <body>
        <h1>业务线列表(对象)</h1>
        <ul>
            {% for row in v1 %}
                <li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li>
            {% endfor %}
        </ul>
    
        <h1>业务线列表(字典)</h1>
        <ul>
            {% for row in v2 %}
                <li>{{ row.id }} - {{ row.caption }}</li>
            {% endfor %}
        </ul>
    
        <h1>业务线列表(元组)</h1>
        <ul>
            {% for row in v3 %}
                <li>{{ row.0 }} - {{ row.1 }}</li>
            {% endfor %}
        </ul>
    </body>

    b.1、一对多跨表操作

    一定要注意:“.”和“__”跨表,在不同场景的应用。

    点跨表,点的是对象,双下划线,是在filter里的字符串中。

    b.2、一对多块表操作的的三种方式

    具体用法看示例,及注释

    b.3、示例:host主机列表

    urls.py

    url(r'^host$', views.host),
    • 1

    views.py

    def host(request):
        v1 = models.Host.objects.filter(nid__gt=0)  # 同 all()
        # QuerySet [host 对象,]
        for row in v1:
            print(row.nid,row.b_id,row.b.caption,sep='	')  # 注意 row.b.caption 的含义
        # 注意:这里通过"__"双下划线跨表。'b__caption'
        v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
        # QuerySet: [ {} ]
        print(v2)
        for row in v2:
            print(row['nid'], row['hostname'],row['b_id'], row['b__caption'])
        # 注意:这里通过"__"双下划线跨表。'b__caption'
        v3= models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
        # QuerySet: [ () ]
    
        b_list = models.Business.objects.all()
        return render(request, 'host.html', {'v1':v1, 'v2':v2, 'v3':v3, 'b_list':b_list})

    host.html

    <body>
        <h1>主机列表(对象)</h1>
        <table border="1">
            <thead>
                <tr>
                    <th>主机名</th>
                    <th>IP</th>
                    <th>端口</th>
                    <th>业务线名称</th>
                </tr>
            </thead>
            <tbody>
                {% for row in v1 %}
                    <tr hid="{{ row.nid }}" bid="{{ row.b.id }}">
    {#                主机id不需要让用户看见,但修改时又需要,所以自定义个属性#}
                        <td>{{ row.hostname }}</td>
                        <td>{{ row.ip }}</td>
                        <td>{{ row.port }}</td>
                        <td>{{ row.b.caption }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
        <hr>
        <h1>主机列表(字典)</h1>
        <table border="1">
            <thead>
                <tr>
                    <th>主机名</th>
                    <th>业务线名称</th>
                </tr>
            </thead>
            <tbody>
                {% for row in v2 %}
                    <tr hid="{{ row.nid }}" bid="{{ row.b.id }}">
                        <td>{{ row.hostname }}</td>
                        <td>{{ row.b__caption }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
        <hr>
        <h1>主机列表(元组)</h1>
        <table border="1">
            <thead>
                <tr>
                    <th>主机名</th>
                    <th>业务线名称</th>
                </tr>
            </thead>
            <tbody>
                {% for row in v3 %}
                    <tr hid="{{ row.0 }}" bid="{{ row.2 }}">
                        <td>{{ row.1 }}</td>
                        <td>{{ row.3 }}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    </body>

    c、增加一对多数据示例

    如果前端展示序号的话,模板语言for循环那里有几个点可以实现。

    {% for row in v1 %}           # 假设3条数据
        {{ forloop.counter}}      # 依次为:1、2、3
        {{ forloop.counter0}}     # 依次为:0、1、2
        {{ forloop.revcounter}}   # 依次为:3、2、1
        {{ forloop.revcounter0}}  # 依次为:2、1、0
        {{ forloop.last }}        # 是否最后一个,是为True,其他False
        {{ forloop.first }}       # 是否第一个
        {{ forloop.parentloop }}  # 嵌套循环,表示上层循环的上面的六个值分别是多少。
    {% endfor %}

    CSS 知识点position回顾:

    • fixed :生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。

    • relative :生成相对定位的元素,相对于其正常位置进行定位。因此,”left:20” 会向元素的 LEFT 位置添加 20 像素。

    • absolute :生成绝对定位的元素,相对于第一个父元素进行定位 (static 定位除外外)。元素的位置通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。


    上面的b.3示例,views里host函数,修改为:

    def host(request):
        if request.method == "GET":
            v1 = models.Host.objects.filter(nid__gt=0)
            v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
            v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
    
            b_list = models.Business.objects.all()
    
            return render(request, 'host.html', {'v1': v1,'v2': v2,'v3': v3,'b_list':b_list})
    
        elif request.method == "POST":
    
            h = request.POST.get('hostname')
            i = request.POST.get('ip')
            p = request.POST.get('port')
            b = request.POST.get('b_id')
    
            models.Host.objects.create(hostname=h,
                                       ip=i,
                                       port=p,
            # b=models.Business.objects.get(id=b)  # 效果一样,只是多了一次数据库查询操作
                                       b_id=b
                                       )
            # renturn render(request, 'host.html')  # 每次post提交都会有刷新操作
            # 使用render需要返回模板数据,if条件里的代码再写一遍,为了提高重用性,让用户再次访问。
            return redirect('/host')

    host.html (部分与上面重复的代码略,行数太多,有些合并为一行了),需要加载jQury。

    <head>
        <style>
            .hide{ display: none; }
            .shade{
                position: fixed;
                top: 0; right: 0; left: 0; bottom: 0;
                background: black;
                opacity: 0.6; z-index: 100;
            }
            .add-modal{
                position: fixed;
                height: 300px; width: 400px; margin-left: -200px; top:100px; left: 50%;
                background: white; border: 1px solid red;
                z-index: 101;
            }
        </style>
    </head>
    <body>
        <h1>主机列表(对象)</h1>
        <div>
            <input id="add_host" type="button" value="添加" />
        </div>
        <table border="1">
            <thead>
                <tr>
                    <th>序号</th>
                    <th>主机名</th>
                    <th>IP</th>
                    <th>端口</th>
                    <th>业务线名称</th>
                </tr>
            </thead>
            <tbody>
                    {% for row in v1 %}
                        <tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
                            <td>{{ forloop.counter }}</td>
                            <td>{{ row.hostname }}</td>
                            <td>{{ row.ip }}</td>
                            <td>{{ row.port }}</td>
                            <td>{{ row.b.caption }}</td>
                        </tr>
                    {% endfor %}
            </tbody>
        </table>
    
        <div class="shade hide"></div>
        <div class="add-modal hide">
            <form method="POST" action="/host">
                <div class="group">
                    <input id="host" type="text" placeholder="主机名" name="hostname" />
                </div>
                <div class="group">
                    <input id="ip" type="text" placeholder="IP" name="ip" />
                </div>
                <div class="group">
                    <input id="port" type="text" placeholder="端口" name="port" />
                </div>
                <div class="group">
                    <select id="sel" name="b_id">
                        {% for op in b_list %}
                        <option value="{{ op.id }}">{{ op.caption }}</option>
                        {% endfor %}
                    </select>
                </div>
                <input type="submit" value="提交" />
                <input id="cancel" type="button" value="取消" />
            </form>
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
        <script>
            $(function(){  // 框架加载完后,自执行
    
                $('#add_host').click(function(){
                    $('.shade,.add-modal').removeClass('hide');
                });
    
                $('#cancel').click(function(){
                    $('.shade,.add-modal').addClass('hide');
                });
            });
        </script>
    </body>

    d、初识Ajax

    上面是通过模态对话框实现的,但是有个弊端,就是弹出框里一点提交,页面肯定会刷新。如果用户输错,应该显示错误提示,而不是刷新页面

    所以希望有个功能可以用户输完之后,悄悄发送数据给后台,页面不刷新。后台拿到之后再给个回复。

    悄悄提交,用Ajax提交。

    这里Ajax 用jQuery来实现。jquery依赖于xml http request 对象来发的。

    $.ajax({
        url: '/host',                    // 提交地址
        type: "POST",                    // 提交方式
        data: {'k1': 123,'k2': "root"},  // 提交数据
        success: function(data){         // data是服务器端返回的字符串
            // 等服务器返回数据后,自动触发这个函数
            var obj = JSON.parse(data);  // 把字符串转换成对象
        }
    })

    Ajax请求return 用HttpResponse

    • obj = JSON.parse(data) 把字符串转换成对象
    • JSON.stringfy(obj) 前端把对象转换成字符串

    host.html 添加:

        <form id="add_form" method="POST" action="/host">
                 .
                 ..
                 ...
                <input type="submit" value="提交" />
                <a id="ajax_submit" >悄悄提交</a>
                <input id="cancel" type="button" value="取消" />
                <span id="erro_msg" style="color: red"></span>
        </form>
        <script>
                $('#ajax_submit').click(function(){
                    $.ajax({
                        url: "/test_ajax",
                        type: 'POST',
                        //data: {'hostname': $('#host').val(), 'ip': $('#ip').val(), 'port': $('#port').val(), 'b_id': $('#sel').val()},
                        data: $('#add_form').serialize(),  // 同上面那一句
                        success: function(data){
                            var obj = JSON.parse(data);
                            if(obj.status){
                                location.reload();;  // JS刷新当前页面
                            }else{
                                $('#erro_msg').text(obj.error);
                            }
                        }
                    })
                });
        </script>

    urls.py

    url(r'^test_ajax$', views.test_ajax),
    • 1

    views.py

    def test_ajax(request):
        import json
        ret = {'status': True, 'error': None, 'data': None}
        try:
            h = request.POST.get('hostname')
            i = request.POST.get('ip')
            p = request.POST.get('port')
            b = request.POST.get('b_id')
            if h and len(h) > 5:
                models.Host.objects.create(hostname=h, ip=i, port=p, b_id=b)
            else:
                ret['status'] = False
                ret['error'] = "太短了"
        except Exception as e:
            ret['status'] = False
            ret['error'] = '请求错误'
        return HttpResponse(json.dumps(ret))

    除了$.ajax ,还有$.get$.post$.getJson等等,这些方法内部都是调用$.ajax

    $.get本质上就是type写成get。$.get(url="xxx", data={}, success……)

    e、编辑一对多示例

    删除数据的话,可以后台删除数据并且页面直接remove掉相应的显示内容,这样页面就不用刷新了。

    修改下面主要是获取select下拉框的值做获取修改

        <div class="edit-modal hide">
            <form id="edit_form" method="POST" action="/host">
                    <input type="text" name="nid" style="display:none" />
              <!--在用户点编辑的一刹那,获取的nid放在这,提交给后台是那条id的数据。-->
                    <input type="text" placeholder="主机名" name="hostname" />
                    <input type="text" placeholder="IP" name="ip" />
                    <input  type="text" placeholder="端口" name="port" />
                    <select name="b_id">
                        {% for op in b_list %}
                        <option value="{{ op.id }}">{{ op.caption }}</option>
                        {% endfor %}
                    </select>
                <a id="ajax_submit_edit" >确认编辑</a>
            </form>
        </div>
    
        <script>
                $('.edit').click(function(){
                    $('.shade,.edit-modal').removeClass('hide');
    
                    var bid = $(this).parent().parent().attr('bid');
                    var nid = $(this).parent().parent().attr('hid');
    
                    $('#edit_form').find('select').val(bid);
                    $('#edit_form').find('input[name="nid"]').val(nid);
    
                    // 修改
                    $.ajax({
                        data: $('#edit_form').serialize()
                    });
                    // models.Host.objects.filter(nid=nid).update()
                })
        </script>

    三、Django ORM 外键操作 多对多

    假设目前有两张表,主机表和应用表,

    class Host(models.Model):
        nid = models.AutoField(primary_key=True)
        hostname = models.CharField(max_length=32,db_index=True)
        ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
        port = models.IntegerField()
    
    class Application(models.Model):
        name = models.CharField(max_length=32)

    一个主机可以对应多个应用、一个应用也可对应多个主机。多个一对多,即多对多

    1、创建多对多

    • 方式一:自定义关系表

    创建第三张关系表。

    class HostToApp(models.Model):
        hobj = models.ForeignKey(to='Host',to_field='nid')
        aobj = models.ForeignKey(to='Application',to_field='id')
    • 方式二:自动创建关系表

    表中添加 ManyToManyField

    class Application(models.Model):
        name = models.CharField(max_length=32)
        r = models.ManyToManyField("Host")

    这样第三张表Django就帮我们创建了。这里表名为 app01_application_r ,这里写了两个类生成三张表。

    总结:一般用哪种方式?

    自动创建关系表,表中只能创建三列数据。想增加额外的列做不到。对于自定义的,自定制功能更好些。

    2、多对多关系表中添加数据

    • 方式一:自定义关系表

    这种添加数据比较简单,就是表中添加数据那样。典型的 使用类操作数据库

    models.User.objects.create(hobj_id=1,aobj_id=2)
    • 方式二:自动创建的关系表

    上面是使用类操作数据库,但是这里自动创建的关系表,没有类,就不能通过类直接操作数据库。

    所以只能通过里面定义的r进行间接操作。

    和之前操作一样,获取到r:

    obj = models.Application.objects.get(id=1)  # 获取id为1的application对象
    obj.name    # 应用名
    obj.r       # 获取到 r 

    获取到r,里面有哪些功能呢?

    obj = models.Application.objects.get(id=1) 
    # 增加数据
    obj.r.add(1)            # 添加application id为1,host id也是1
    obj.r.add(2)            # 添加application id为1,host id也是2
    obj.r.add(2,3,4)        # 增加多条关系1 2,1 3 ,1 4
    obj.r.add(*[1,2,3,4])   # 增加列表,增加多条关系1 1 ,1 2,1 3 ,1 4
    # 删除数据
    obj.r.remove(1)         # 删除 1 1 记录
    obj.r.remove(2,4)       # 删除 1 2,1 4 记录
    obj.r.remove(*[1,2,3])  # 通过列表删除
    
    obj.r.clear()           # 删除application id为1的所以数据
    # 更新、修改数据(注意这里列表不加*)
    obj.r.set([3,5,7])      # 只存在1 3,1 5,1 7。application id=1的其他都没有了
    
    # 查询数据
    obj.r.all()             # 所有application id=1的主机对象 “列表”QuerySet类型

    3、多对多数据增加示例

    views.py

    def app(request):
        if request.method == "GET":
            app_list = models.Application.objects.all()
            host_list = models.Host.objects.all()
            return render(request,'app.html',{"app_list": app_list,'host_list': host_list})
        elif request.method == "POST":
            app_name = request.POST.get('app_name')
            host_list = request.POST.getlist('host_list')
            print(app_name,host_list)
            # 创建完之后会返回一个对象。对象就是你增加的这条数据的对象。
            obj = models.Application.objects.create(name=app_name)
            obj.r.add(*host_list)
            return redirect('/app')
    
    def ajax_add_app(request):
        ret = {'status':True, 'error':None, 'data': None}
        app_name = request.POST.get('app_name')
        host_list = request.POST.getlist('host_list')
        obj = models.Application.objects.create(name=app_name)
        obj.r.add(*host_list)
        return HttpResponse(json.dumps(ret))

    urls.py

    url(r'^app$', views.app),
    url(r'^ajax_add_app$', views.ajax_add_app),

    app.html

        <div class="add-modal hide">
            <form id="add_form" method="POST" action="/app">
                <div class="group">
                    <input id="app_name" type="text" placeholder="应用名称" name="app_name" />
                </div>
                <div class="group">
                    <select id="host_list" name="host_list" multiple>
                    <!--这里,示例中增加数据,之前是单选的,多对多这里要改成多选的。multiple-->
                        {% for op in host_list %}
                            <option value="{{ op.nid }}">{{ op.hostname }}</option>
                        {% endfor %}
                    </select>
                </div>
                <input type="submit" value="提交" />
                <input id="add_submit_ajax" type="button" value="Ajax提交" />
            </form>
        </div>
    
        <script>
                    $('#add_submit_ajax').click(function(){
                    $.ajax({
                        url: '/ajax_add_app',
                        // data: {'user': 123,'host_list': [1,2,3,4]},
                        data: $('#add_form').serialize(),
                        type: "POST",
                        dataType: 'JSON', // 内部
                        traditional: true,  // 获取到普通字符和列表,否则列表发不到后台
                        success: function(obj){
                            console.log(obj);
                        },
                        error: function () {  // 发到后台后台没有捕捉到,执行的错误
                        }
                    })
                });
          </script>

    下拉框select,默认val获取单值,如果设了multiple,获取的就是列表。


    转载请务必保留此出处:http://www.cnblogs.com/lgeng/articles/7365134.html 

    <!-- END  -->

    《版权说明》: 本文转自-- http://blog.csdn.net/fgf00/article/details/53812643

  • 相关阅读:
    jenkins集成python时出现"Non-ASCII character 'xe6' in file"错误解决方法
    【转】/bin/bash^M: bad interpreter: No such file or directory
    解决Jenkins中执行jmeter脚本后不能发报告(原报告被覆盖、新报告无法保存)的问题
    【转】shell脚本中如何传入参数
    Hibernate学习笔记
    struts2学习笔记
    oracle表空间相关SQL语句
    javaMail
    Mysql 5.7.7
    设计模式之享元模式
  • 原文地址:https://www.cnblogs.com/lgeng/p/7365134.html
Copyright © 2020-2023  润新知