• Django基础--2


    一、路由系统 URL

    1.模板语言循环字典

    1.简单的字典循环

    View Code
    <ul>
            {% for i in user_dict %}
            <li>{{ i }}</li>
            {% endfor %}
        </ul>
    
    获取字典中的key
    
    <ul>
            {% for i in user_dict.keys %}
            <li>{{ i }}</li>
            {% endfor %}
        </ul>
    
    获取字典中的key,通过keys关键字,不要括号
    
    <ul>
            {% for i in user_dict.values %}
            <li>{{ i }}</li>
            {% endfor %}
        </ul>
    
    获取字典中的value,通过values关键字,不要括号
    
    
    <ul>
            {% for i in user_dict.items %}
            <li>{{ i }}</li>
            {% endfor %}
        </ul>
    
    获取字典中的key和value,得到的是个元组
    
    
    <ul>
            {% for i,j in user_dict.items %}
            <li>{{ i }}---{{ j }}</li>
            {% endfor %}
        </ul>
    分别获取字典中的key,value,

                                                                                                                                              

    {% for i in user_dict %}      {% for i in user_dict.keys %}     {% for i in user_dict.values %}          {% for i in user_dict.items %}         {% for i,j in user_dict.items %}

    循环字典,和python里是差不多的,就是后面没有括号():

    • 直接dict :循环的是key
    • dict.keys :循环key
    • dict.values :循环values
    • dict.items :循环key和values

    2.一条对应关系对应多个页面

    这里通过嵌套字典来实现

    场景:一个页面会显示很多用户的某个信息,然后我们点击查看详情,会出来用户的多条信息,但是这些用户信息不是在同一个页面上,例如我们看博客园里面的博客,每次查看如,

    https://www.cnblogs.com/mlq2017/p/10054661.html        /10054661这个值,换一篇博客,这个nid就会变,

    1.通过GET方式实现:

    #index.html
    
    <body>
        <ul>
            {% for i,j in user_dict.items %}
            <li><a target="_blank" href="/detail/?nid={{ i }}">{{ j.name}}</a></li>
            {% endfor %}
    
        </ul>
    </body>
    
    #detail,html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>详细信息</h1>
        <h3>用户名:{{ user_info.name }}</h3>
        <h3>年龄:{{ user_info.age }}</h3>
        <h3>邮箱:{{ user_info.email }}</h3>
    </body>
    </html>
    
    #urls.py
    
    path('index/', views.index),
    path('detail/', views.detail),
    
    
    #wiews.py
    from django.shortcuts import render
    USER_DICT = {
        "1":{"name":"root1","age":"12","email":"123@163.com"},
        "2":{"name":"root2","age":"13","email":"223@163.com"},
        "3":{"name":"root3","age":"14","email":"323@163.com"},
        "4":{"name":"root4","age":"15","email":"423@163.com"},
    }
    
    def detail(request): #通过GET,html里面用  /?+{{key}}
        a = request.GET.get("nid")
        user_info = USER_DICT[a]
        print(user_info)
        return render(request,"detail.html",{"user_info":user_info})
    
    def index(request):
    
        return render(request,"index.html",{"user_dict":USER_DICT})
    View Code

    这里用到了如下方法:

    1.html里面使用:href="/detail/?nid={{ i }}"   通过?加上字典的key跳转链接

    2.views里面的detail里面通过GET获取nid:  然后通过nid获取字典的值,将值传给rengder(),再后在页面获取每个值中的信息

    2.通过正则方式实现:

    上面的HTML代码只有一个地方需要修改,做成动态匹配

    {% for i,j in user_dict.items %}
            <li><a target="_blank" href="/detail-{{ i }}.html">{{ j.name }}</a></li>
            {% endfor %}

    相应的urls.py里面用到正则匹配

    from django.contrib import admin
    from django.urls import path,re_path
    from app import views
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('index/', views.index),
        re_path('detail-(d+).html', views.detail),
    
    ]

    既然我们用到正则,那这里就要导入正则,使用正则匹配

    from django.urls import re_path
    下面的匹配为:
    re_path('detail-(d+).html', views.detail),
    这里要注意,之前的视频是很老的,方法不一样,按照视频上说的做,会一直报错

    然后是views.py里面的修改了

    def detail(request,nid):  #正则匹配
        #return HttpResponse(nid)
        user_info = USER_DICT[nid]
        print(nid)
        return render(request,"detail.html",{"user_info":user_info})

    这里我们需要在detail里面再给一个形参,接收传过来的动态参数:这里返回的是字典的key,

    然后通过nid直接获取字典的第一层值

    建议使用正则的时候用下面的方法

    re_path('detail-(?P<nid>d+).html', views.detail),

    将形参进行绑定,就不用考虑传入的位置

    1.当html后面接很多-(d+)时,如,re_path('detail-(d+)-(d+).html', views.detail),在detail里面传多个形式参数的时候def detail(request,nid,uid): 位置就要一一对应,

          如果用上面的方式,我们就可以任意写形参的位置

    2.当我们写正则的时候绑定了多个参数,在detail里面传多个形式参数的时候def detail(request,nid,uid,……):,我们就要穿多个参数,对于这样的,我们可以使用

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

    这样,我们通过re_path('detail-(d+)-(d+).html', views.detail) 这样的就会把值传给*args

       我们通过re_path('detail-(?P<nid>d+).html', views.detail),这样的就会吧值传给**kwargs

    总结实战:

    a.

      re_path('detail-(d+)-(d+).html', views.detail),

      def func(request,nid,uid):

      def func(request,*args):

      args = (2,9)

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

    b.

      re_path('detail-(?P<nid>d+)-(?P<nid>d+).html', views.detail),

      def func(request,nid,uid):

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

      *args为空,**kwargs为字典接受上面的值

    三.关于url中的name

    1.当频繁修改url,就要修改urls.py,又要修改html页面,

      我们可以在urls里面给url进行绑定,之后无论怎么修改,只需要修改url里面的值就行

    在绑定是添加name进行绑定
    path('index/', views.index,name = "indexx"), 在detail.html里面,提交表单时,这么改 <form action="{% url "indexx" %}"> <input type="text" /> </form>

    2.当我们还要在绑定的url后面继续使用匹配,如:path('index/(d+)/', views.index,name = "indexx"),

      此时我们无法通过action="{% url "indexx" %}" 进行提交,可以用

      1.跳转到固定的页面

    <form action="{% url "indexx" 11 %}">

      后面这个11,表示只能跳转到这个页面,并且这种模板语言,后面只能写固定值

      2.跳转到当前页面

        这里要用到request中的path_info,获取当前页面的url

    在detail函数里面,我们试着打印request.path_info
    
    print(request.path_info) # 输出结果为:/detail-1.html

      distail里面不需要改,因此我们可以在action里面这么写

    <form action="{{ request.path_info }}">

    用 path() 方法实现捕获参数

    课上讲的是旧版本,现在Django已经2.0了,url()方法被path()方法替代,用法也有区别。
    re_path() 可以看做是2.0里向下兼容的一个方法,也就是旧的1.0的 url() 方法。在2.0里用 path() 方法也可以实现捕获组的对应关系。使用 path() 方法需要注意:

      1. 要捕获一段url中的值,需要使用尖括号,而不是之前的圆括号;
      2. 可以转换捕获到的值为指定类型,比如int。默认情况下,捕获到的结果保存为字符串类型,不包含 '/' 这个特殊字符;
      3. 匹配模式的最开头不需要添加 '/' ,因为默认情况下,每个url都带一个最前面的 '/' ,既然大家都有的部分,就不用浪费时间特别写一个了。
     path('detail2-<int:nid>-<int:uid>.html', views.detail2),

    上面的例子,就是捕获一个0或正整数,并且返回一个int类型,再用冒号把命名也完成了。除了int,还有下面这些。
    默认情况下,Django内置下面的路径转换器:

      • str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的;
      • int:匹配0和正整数,返回一个int类型;
      • slug:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如’ building-your-1st-django-site‘ ;
      • uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象;
      • path:匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。

    小结

    上面各种实现的方法由浅入深,并且一个比一个好,推荐用最后面的实现方式:

    • 基于正则的url比使用get方式获取参数的好
    • 命名捕获组比普通捕获组好
    • 推荐还是用最后的 path() 方法来实现,如果是1.x的版本,那么就是推荐基于正则的命名捕获组的方法。

    另外,在定义函数的时候也可以写成这种万能的模式: def detail2(request, *args, **kwargs): ,这样的话,要使用 args[0] (普通捕获组)或 kwargs['nid'] (命名捕获组)来取值。

    路由分发

    之前所有的url对应关系都写在了项目名下的urls里面,当我们的项目有多个app的时候,所有的页面都写在一起也不好。应该是每个app各自管理自己的那部分url。这就需要路由分发。

    1.首先我们在项目名下的urls里面导入include,

    from django.contrib import admin
    from django.urls import path,re_path
    from django.urls import include      #导入include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('app/',include('app.urls'))   #添加app的绑定
    ]

        如果有多个app,就继续在下面添加绑定  path('app1/',include('app1.urls')) 这样的

    2.然后在app里面创建一个同样的urls.py

    3.导入views,py

    from django.urls import path,re_path
    from app import views
    urlpatterns = [
        path('index/', views.index),
    ]

    默认值

    命名空间

    二、视图

    提交数据不仅有POST和GET,还有其他的提交方法,比如:PUT、DELETE、HEAD、OPTION

    form表单提交数据

    1,request.POST.get()

    2,request.GET.get()

    3,request.POST.getlist()

    4,request.FILES.get()

    先上代码:

    #login。html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="" method="post" enctype="multipart/form-data">
            <p>
                <input type="text" name="user" placeholder="用户名">
            </p>
            <p>
                <input type="password" name="pwd" placeholder="密码">
            </p>
            <p>
                男:<input type="radio" name="gender" value="1">
                女:<input type="radio" name="gender" value="2">
                人妖:<input type="radio" name="gender" value="3">
            </p>
            <p>
                男:<input type="checkbox" name="favl" value="11">
                女:<input type="checkbox" name="favl" value="22">
                人妖:<input type="checkbox" name="favl" value="33">
            </p>
            <p>
                <select name="city" multiple="multiple">
                    <option  value="sz">深圳</option>
                    <option value="bj">北京</option>
                    <option value="sh">上海</option>
                </select>
            </p>
            <p>
                <input name="file" type="file">
            </p>
            <p>
                <input type="submit" value="提交">
                <span style="color: red">{{ error_msg }}</span>
            </p>
    
    
        </form>
    </body>
    </html>
    
    
    views.py
    
    from django.shortcuts import render,redirect,HttpResponse
    
    # Create your views here.
    
    def login(request):
        if request.method == "POST":
            obj = request.POST.get("gender")
            print("obj",obj) #输出为1
            obj1 = request.POST.get("favl")
            obj2 = request.POST.getlist("favl")
            print("obj1",obj1)  #这里checkbox是多选框,但是只获取到一个值,实际要获取多个,这里不能用get
            print("obj2", obj2)  #obj2 ['11', '33'] 这里获取到了每个选中的值
            obj3 = request.POST.getlist("city")
            print("obj3", obj3)  #obj3 ['sz', 'bj', 'sh']
            obj4 = request.POST.get("file")  #obj4 None <class 'NoneType'> 这里没获取到
            print("obj4", obj4,type(obj4))
            obj5 = request.FILES.get("file")
            print("obj5", obj5, type(obj5))  #这里获取到的是文件名和文件,
            #from django.core.files.uploadedfile import InMemoryUploadedFile
            f = open(obj5.name,"wb")
            for data in obj5.chunks():
                f.write(data)
            f.close()
        elif request.method == "GET":
            pass
        else:
            return HttpResponse("index")
    
        return render(request,"login.html",)
    
    #error_msg = ""
    # def login(request):
    #     if request.method == "POST":
    #         u = request.POST.get("user")
    #         p = request.POST.get("pwd")
    #         if u == 'aaa' and p == '123':
    #             return HttpResponse("home")
    #         else:
    #             error_msg = "用户名或密码错误"
    #             return render(request,"login.html",{"error_msg":error_msg})
    #     elif request.method == "GET":
    #         pass
    #     else:
    #         return HttpResponse("index")
    #
    #     return render(request,"login.html",)
    
    
    #urls.py
    
    from app import views
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
    ]
    View Code

    注释部分第一节已经写过,都是通过get获取

    对于从网页只获取一个值的我们可以用get(text,password,radio),

    obj = request.POST.get("gender")
            print("obj",obj) #输出为1

    但是如果是多选,就需要用getlist(checkbox,select),

     obj1 = request.POST.get("favl")
            obj2 = request.POST.getlist("favl")
            print("obj1",obj1)  #这里checkbox是多选框,但是只获取到一个值,实际要获取多个,这里不能用get
            print("obj2", obj2)  #obj2 ['11', '33'] 这里获取到了每个选中的值
     obj3 = request.POST.getlist("city")
            print("obj3", obj3)  #obj3 ['sz', 'bj', 'sh']

    普通表单无法上传文件,要上传文件,还需要在form标签中加  enctype="multipart/form-data"

    另文件我们需要用request.FILES.get(),这样获取到的不是文件的名称,而是一个类,既能获取到文件名,又能获取到文件,文件名通过obj5.name获取,文件内容用 obj5.chunks()

    obj4 = request.POST.get("file")  #obj4 None <class 'NoneType'> 这里没获取到
            print("obj4", obj4,type(obj4))
            obj5 = request.FILES.get("file")
            print("obj5", obj5, type(obj5))  #obj5 作业.png <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>这里获取到的是文件名和文件,
            #from django.core.files.uploadedfile import InMemoryUploadedFile
            f = open(obj5.name,"wb")
            for data in obj5.chunks():
                f.write(data)
            f.close()

    CBV 和 FBV

    • FBV(function base views) 就是在视图里使用函数处理请求。
    • CBV(class base views) 就是在视图里使用类处理请求。

    到目前为止,所有的处理都是写在一个函数里的。Django还提供另外一个方式,我们也可以通过类来处理。

    之前用FBV,这里也可以把之前的函数改成类处理请求,

    #home.html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="/home/" method="post">
            <input type="text" name="user" />
            <input type="submit" value="提交" />
        </form>
    </body>
    </html>
    
    
    #views.py
    
    from django.shortcuts import render,redirect,HttpResponse
    
    
    #FBV和CBV
    from django.views import View
    
    class Home(View):
    
        def get(self,request):  #当输入http://127.0.0.1:8000/home/地址时,是get请求,因此下面的打印的结果为GET
            print(request.method)
            return render(request,"home.html")
    
        def post(self,request):
            print(request.method) #当输入内容时,是post请求,执行了这里,打印结果为POST
            return render(request,"home.html")
    
    
    
    #urls.py
    
    from app import views
    urlpatterns = [
        path('admin/', admin.site.urls),
        #path('login/', views.login), 提交表单的
        path('home/', views.Home.as_view()),
    
    ]
    View Code

    以上代码执行和之前使用FBV效果是一样的,我们在类里写上对应的提交方式,当执行是,会自动到类里面去找对应的函数。

    CBV的写法为

    1.先导入View,在视图里面创建类

    from django.views import View
    
    class Home(View):

    2.修改urls里面,这里添加url对应关系和FBV不同,应此需要特别注意:中间写上类名,后面固定跟一个 .as_view()

    3.在类中定义相应的方法,

    先去看看继承的View类里有什么,在源码的base.py这个文件里。首先里面定义了一个公有属性:

    点击View会看到

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    所以除了GET,POST,还可以处理这么多的请求方法,用起来,在类里照着别的一样定义一个同名方法就可以了。

    3.在类中可以引入父类,就是Django内部定义的父类,这样我们既能定义自己的类,还可以继承父类,修改父类(装饰器)

    继续看源码的View,调用了as_view()方法里面会再调用一个dispatch()方法。这个dispatch()方法里是通过映射获取我们的 request.method 即提交的方法来调用我们的处理方法的。dispatch()的源码如下:

        def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
    View Code

    结论就是,根据不同的请求类型提交到不同的处理方法,是用过dispatch()方法里通过映射来实现的。先执行dispatch()方法然后再调用对应的提交类型的处理方法。

    class Home(View):
    
        def dispatch(self, request, *args, **kwargs):
            #调用父类中的dispatch
            result = super(Home,self).dispatch(request, *args, **kwargs)
            return result
    
        def get(self,request):  #当输入http://127.0.0.1:8000/home/地址时,是get请求,因此下面的打印的结果为GET
            print(request.method)
            return render(request,"home.html")
    
        def post(self,request):
            print(request.method) #当输入内容时,是post请求,执行了这里,打印结果为POST
            return render(request,"home.html")
    View Code

    在类中添加dispatch()方法后,执行效果和不添加它的效果是一样的

    所以通过继承和重构dispatch()方法,可以在处理方法执行前和执行后自定义一些操作。如果需要的话就在我们的类里继承并重构,

        def dispatch(self, request, *args, **kwargs):
            #调用父类中的dispatch
            print("before")  //处理前执行的操作
            result = super(Home,self).dispatch(request, *args, **kwargs)
            print("after") //处理后执行的操作
            return result

    上面看了View代码,我们看到了,当提交方式都不匹配的时候是执行下面的代码,页面是返回405

        def http_method_not_allowed(self, request, *args, **kwargs):
            logger.warning(
                'Method Not Allowed (%s): %s', request.method, request.path,
                extra={'status_code': 405, 'request': request}
            )

    这里我们就可以重构自己的方法了:

    def http_method_not_allowed(self, request, *args, **kwargs):
            return redirect('/home/')

    给views.py分类

    默认所有的处理函数都是写在views.py这个文件里的。如果处理函数很多,全部写在一个文件里也会很乱。这是可以考虑创建一个views包来替代原来的views.py文件。然后在views包里创建多个py文件来写我们的处理函数。比如:

    • views/account.py 是用户相关的操作,登录认证之类的
    • views/test.py 是用来测试的处理函数
    • views/order.py 订单相关的操作

    三、模板

     

    
    
    
    
    
    
     

    四、ORM操作

    django数据库错误相关问题

    连接sqlite数据库

    默认使用的是sqlite3作为数据库,使用数据库需要一下步骤
    一、创建你的数据库表结构

    在app01里面的models.py添加类

    from django.db import models
    # Create your models here.
    class Userinfo(models.Model):
        username = models.CharField(max_length=32);
        password = models.CharField(max_length=64);

    上面的类等到去数据库创建表的时候,表名是 “app01_userinfo” ,也就是 [app名]_[类名] 。

    二、设置settings.py文件
    在 INSTALLED_APPS 注册你的app,把你的app追加到这个列表里:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.AppConfig',
    ]
    View Code

                      注:如果是在pycharm里面创建项目的时候创建的app,默认是已经加上了的;通过命令添加的,需要上面的操作:'app02.apps.AppConfig',

    配置数据库连接,默认已经配置好了一个sqlite3,所以不需要修改:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    View Code

    三、去终端执行2条命令

    python manage.py makemigrations
    python manage.py migrate

    第一条命令会在 app 目录下的 migrations 目录下创建一个文件(0001_initial.py),记录我们对 models.py 所做的变动。
    第二条命令是真正去操作数据库了,除了创建我们自己写的表以外,还创建了很多 django 自己的表。
    上面两条命令都是作用于全局的,如果要限定作用于只在某个app,可以在最后加上app的名称:

    python manage.py makemigrations app01
    python manage.py migrate app01

    关于SQLite:

    SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。
    Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。

    使用SQLite

    安装Navicat Premium 可以连接上SQLite

    1.在pycharm里面右键点击SQLite,然后点击 copy path

    2.打开Navicat Premium  ,点击连接,选择SQlite,将C:UsersYPPycharmProjectsday19db.sqlite3  复制到文件路径里面,给连接取个名字。用户名和密码不用填,点击连接

    打开后,效果如下面截图

     连接mysql

    在settings里面设置如下:

    DATABASES = {
        'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'dbname',
        'USER': 'root',
        'PASSWORD': 'xxx',
        'HOST': '',
        'PORT': '',
        }
    }
    1
    2
    3
    4
    5
    6
        
    # 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
      
    # 如下设置放置的与project同名的配置的 __init__.py文件中
      
    import pymysql
    pymysql.install_as_MySQLdb() 
    View Code

    注:上面的数据库名,使用mysql时,需要自己手动创建

     数据的增删改查

    ORM操作

    先添加如下代码:

    #在APP01里面的urls.py里面
    
    path("orm/", views.orm),
    
    #views.py
    from app01 import models
    
    def orm(request):
    
    
        return HttpResponse("orm")

    既然要操作数据库,这里肯定要导入models模块,表就用之前讲的userinfo

    1.1添加数据,网页上输入http://127.0.0.1:8000/app01/orm/后,使用Navicat Premium 看下表里面的内容,此时已经添加了三条数据

        #添加数据
        for row in list:
        list = [
        {"username": "root", "password": "admin"},
        {"username": "admin", "password": "admin"},
        {"username": "user", "password": "123456"},
          ]
    models.Userinfo.objects.create(username
    =row['username'],password=row["password"]) return HttpResponse("ORM")

    1.2添加数据

        # 第二种方法
         obj = models.Userinfo(username="root",password="123456")
         obj.save()

    1.3添加数据

        # 第三种方法
        dic = {"username": "root", "password": "admin"}
        models.Userinfo.objects.create(**dic)

    2.查询数据

        #查询数据all.filter
        result = models.Userinfo.objects.all() #查询所有
        result = models.Userinfo.objects.filter(username="root").all()   #while条件,查询指定的数据
        for i in result:
            print(i.id,i.username,i.password)

    filter()里面还可以传入多个参数,就是多个条件,他们之间的关系是逻辑与(and)。

    还有一个first()方法,取出第一个值,这样返回就不是列表而直接就是对象了。可以直接用,也可以用在filter()方法后面

    models.UserInfo.objects.first()
    models.UserInfo.objects.filter(username='root', password='123456').first()
    # 上面就是一个验证用户登录的逻辑了,返回结果是None或者是找到的第一个对象

    QuerySet 对象,分别打印出查询结果和这个对象的query属性:

    res = models.UserInfo.objects.all()
    print(res)  # 结果在下面
    # <QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>, <UserInfo: UserInfo object (3)>]>
    print(res.query)  # 结果写下面
    # SELECT "cmdb_userinfo"."id", "cmdb_userinfo"."username", "cmdb_userinfo"."password" FROM "cmdb_userinfo

    因此要看到对象的值,需要用for循环每个元素

    3.删除数据

        #删数据 delete
         models.Userinfo.objects.filter(username="admin").delete()

    4.修改数据

        #改数据update
        models.Userinfo.objects.filter(username="root").update(password = "123456")

     后台管理实例

    修改settings

    #settings中的设置
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        #'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    去掉注释的部分
    
    绑定mysql
    
    DATABASES = {
        'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'testdb',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': '127.0.0.1',
        'PORT': '',
        }
    }
    View Code

    在总urls里面导入include

    # 总urls.py
    from django.contrib import admin
    from django.urls import path
    from django.urls import include
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('ORM/', include("ORM.urls")),
    ]
    
    #orm中的urls
    
    from django.urls import path,re_path
    from ORM import views
    urlpatterns = [
        path('login/', views.login),
        path('home/', views.home),
        path('user_info/', views.user_info),
        re_path('detail-(?P<nid>d+)', views.detail),
    ]
    View Code

    mysql需要在项目名下的项目里面修改__init__

    import pymysql
    
    pymysql.install_as_MySQLdb()
    View Code

    添加三个页面

    #login
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="/ORM/login/" method="post" enctype="multipart/form-data">
            <p>
                <input type="text" name="user" placeholder="用户名">
            </p>
            <p>
                <input type="password" name="pwd" placeholder="密码">
            </p>
            <p>
                <input type="submit" value="提交">
                <span style="color: red">{{ error_msg }}</span>
            </p>
    
    
        </form>
    </body>
    </html>
    
    
    #home
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            body{
                margin: 0 auto;
            }
            .head{
                height: 48px;
                background-color: #66c9f3;
            }
            .left{
                 200px;
                position: absolute;
                left: 0;
                top: 48px;
                bottom: 0;
                background-color: #dddddd;;
            }
            .menu{
                display: block;
                padding: 10px;
            }
            .right{
                background-color: #bce8f1;
                position: absolute;
                top: 48px;
                left: 200px;
                right: 0px;
                bottom: 0;
            }
        </style>
    </head>
    <body>
        <div class="head">后台管理系统</div>
        <div class="left">
            <a class="menu" href="/ORM/user_info/">用户管理</a>
            <a class="menu" href="/ORM/user_group/">用户组管理</a>
        </div>
        <div class="right"></div>
    </body>
    </html>
    
    #user_info
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            body{
                margin: 0 auto;
            }
            .head{
                height: 48px;
                background-color: #66c9f3;
            }
            .left{
                 200px;
                position: absolute;
                left: 0;
                top: 48px;
                bottom: 0;
                background-color: #dddddd;;
            }
            .menu{
                display: block;
                padding: 10px;
            }
            .right{
                background-color: #bce8f1;
                position: absolute;
                top: 48px;
                left: 200px;
                right: 0px;
                bottom: 0;
            }
        </style>
    </head>
    <body>
        <div class="head">后台管理系统</div>
        <div class="left">
            <a class="menu" href="/ORM/user_info/">用户管理</a>
            <a class="menu" href="/ORM/user_group/">用户组管理</a>
        </div>
        <div class="right">
            <h3>用户列表</h3>
            <table border="1px">
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>用户名</th>
                    </tr>
                    {% for row in userlist%}
                        <tr>
                            <td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
                            <td>{{row.username}}</td>
                        </tr>
                    {% endfor %}
                </thead>
            </table>
        </div>
    </body>
    </html>
    
    
    
    #detail
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            body{
                margin: 0 auto;
            }
            .head{
                height: 48px;
                background-color: #66c9f3;
            }
            .left{
                 200px;
                position: absolute;
                left: 0;
                top: 48px;
                bottom: 0;
                background-color: #dddddd;;
            }
            .menu{
                display: block;
                padding: 10px;
            }
            .right{
                background-color: #bce8f1;
                position: absolute;
                top: 48px;
                left: 200px;
                right: 0px;
                bottom: 0;
            }
        </style>
    </head>
    <body>
        <div class="head">后台管理系统</div>
        <div class="left">
            <a class="menu" href="/ORM/user_info/">用户管理</a>
            <a class="menu" href="/ORM/user_group/">用户组管理</a>
        </div>
        <div class="right">
            <h3>详细信息</h3>
            <h5>id:{{ user.id }}</h5>
            <h5>用户名:{{ user.username }}</h5>
            <h5>密码:{{ user.password }}</h5>
        </div>
    </body>
    </html>
    View Code

    models创建表

    from django.db import models
    
    # Create your models here.
    class User(models.Model):
        username = models.CharField(max_length=16)
        password = models.CharField(max_length=32)

    去终端执行2条命令

    python manage.py makemigrations
    python manage.py migrate

    views主要代码:

    from django.shortcuts import render,HttpResponse,redirect
    from ORM import models
    
    def initialized_db():
        USER = [
            {"username": "aaa", "password": "1234"},
            {"username": "bbb", "password": "1234"},
            {"username": "ccc", "password": "1234"},
            {"username": "ddd", "password": "1234"},
        ]
        find = models.User.objects.first()
        print("find",find)
        if find == None:
            for row in USER:
                models.User.objects.create(username =row["username"],password = row["password"])
    initialized_db()
    
    def login(request):
        error_msg = ""
        if request.method == "GET":
            return render(request,"login.html")
        elif request.method == "POST":
            user = request.POST.get("user",None)
            pwd = request.POST.get("pwd",None)
            result = models.User.objects.filter(username = user,password = pwd).first()
            #print(result)
            if result:
                return redirect("/ORM/home/")
            else:
                error_msg = "用户名或密码错误"
                return render(request,"login.html",{"error_msg":error_msg})
        else:
            return redirect("/login/")
    
    def home(request):
    
        return render(request,"home.html")
    
    def user_info(request):
        userlist = models.User.objects.all()
        #print(userlist.query)  #打印出来的是select语句
        return render(request,"user_info.html",{"userlist":userlist})
    
    def detail(request,nid):
        a = models.User.objects.filter(id=nid)
        user = models.User.objects.filter(id = nid).first()  #这里要用first,否则就要用循环
        #print(type(a),type(user))   #<class 'django.db.models.query.QuerySet'> <class 'ORM.models.User'>
        return render(request,"detail.html",{"user":user})

     增加用户信息

    在页面上添加单条用户信息,并立即显示在页面上

    #user_info 添加如下:
    
    <h3>添加用户</h3>
            <form method="post" action="/ORM/user_info/">
                <input type="text" name = "user" placeholder="用户名" />
                <input type="password" name = "password" placeholder="密码" />
                <input type="submit" value="添加" />
            </form>
    
    
    views.py 添加并修改如下:
    
    def user_info(request):
        if request.method == "GET":
            userlist = models.User.objects.all()
            #print(userlist.query)  #打印出来的是select语句
            return render(request,"user_info.html",{"userlist":userlist})
        else:
            if request.method == "POST":
                username = request.POST.get("user")
                password = request.POST.get("password")
                models.User.objects.create(username = username,password = password)
                return redirect("/ORM/user_info/")

    删除用户信息

    修改user_info.html,
    #直接修改表格,也可以再添加一列来放删除
    
    <tr>
       <td><a href="/ORM/detail-{{row.id}}">{{row.id}}</a></td>
       <td>{{row.username}} |
           <a href="/ORM/dele-{{row.id}}">删除</a>
       </td>
    </tr>
    
    
    urls.py 添加如下:
    
    re_path('dele-(?P<nid>d+)', views.dele),
    
    #views.py 添加如下
    
    def dele(request,nid):
        models.User.objects.filter(id = nid).delete()
        return redirect("/ORM/user_info/")

    修改用户信息

    #继承home页面后,并修改右侧内容为
    
    <div class="right">
            <h3>编辑用户信息</h3>
            <form action="user_edit-{{obj.id}}.html" method="post">
                <input style="display: none" type="text" name="id" value="{{obj.id}}"/>
                <input type="text" name="username" value="{{obj.username}}"/>
                <input type="text" name="password" value="{{obj.password}}"/>
                <input type="submit" value="提交"/>
            </form>
        </div>
    
    #urls.py
    
     re_path('user_edit-(?P<nid>d+)', views.user_edit),
    
    #views.py
    
    def user_edit(request,nid):
        if request.method == "GET":
            obj = models.User.objects.filter(id = nid).first()
            return render(request,"user_edit.html",{"obj":obj})
        else:
            if request.method == "POST":
                nid = request.POST.get("id")
                username = request.POST.get("username")
                password = request.POST.get("password")
                models.User.objects.filter(id = nid).update(username = username,password = password)
                return redirect("/ORM/user_info/")

     注意:<form action="user_edit-{{obj.id}}.html" method="post">  这里的写法

    ORM表结构

    修改表结构

    1.修改过表结构之后,需要再执行一下下面的2行命令,把新的表结构应用到数据库。

    python manage.py makemigrations
    python manage.py migrate

    2.修改数据长度:当修改数据长度比之前长的没什么影响,但是修改后的数据长度比之前短,就会造成数据丢失

    3.删除一列,重新执行命令就后自动删掉了,没什么特别的

    4.增加一列:当增加一列

    # Create your models here.
    class User(models.Model):
        username = models.CharField(max_length=16)
        password = models.CharField(max_length=32)
        email = models.CharField(max_length=32)

      在终端执行第一条命令时,会提示,这是因为Django默认内容不能为空,这时你可以根据提示操作

      1.在终端输入字符串作为默认值,然后再输入第二条命令

      2.在新添加的一列后面添加null = True,此时表格新添加的一列就都可以为空了

        email = models.CharField(max_length=32,null=True)

      5.数据类型

      基本数据类型:字符串,数字,时间,二进制

    Django的ORM提供了非常多的字段类型,比如:EmailField、URLField、GenericIPAddressField。这些其实都是字符串类型而已,并且确实对我们没任何用(并不能帮我们做数据验证)。这些字段类型的只有在用Django的后台管理页面 admin 的时候才能发挥数据验证的效果。只有通过admin提交数据的时候才会验证你的数据格式是否正确。后面写怎么登录及哪里会显示格式错误

    models对象的数据类型

    AutoField(Field)
            - int自增列,必须填入参数 primary_key=True
    
        BigAutoField(AutoField)
            - bigint自增列,必须填入参数 primary_key=True
    
            注:当model中如果没有自增列,则自动会创建一个列名为id的列
            from django.db import models
    
            class UserInfo(models.Model):
                # 自动创建一个列名为id的且为自增的整数列
                username = models.CharField(max_length=32)
    
            class Group(models.Model):
                # 自定义自增列
                nid = models.AutoField(primary_key=True)
                name = models.CharField(max_length=32)
    
        SmallIntegerField(IntegerField):
            - 小整数 -32768 ~ 32767
    
        PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正小整数 0 ~ 32767
        IntegerField(Field)
            - 整数列(有符号的) -2147483648 ~ 2147483647
    
        PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
            - 正整数 0 ~ 2147483647
    
        BigIntegerField(IntegerField):
            - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
    
        自定义无符号整数字段
    
            class UnsignedIntegerField(models.IntegerField):
                def db_type(self, connection):
                    return 'integer UNSIGNED'
    
            PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
                'AutoField': 'integer AUTO_INCREMENT',
                'BigAutoField': 'bigint AUTO_INCREMENT',
                'BinaryField': 'longblob',
                'BooleanField': 'bool',
                'CharField': 'varchar(%(max_length)s)',
                'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
                'DateField': 'date',
                'DateTimeField': 'datetime',
                'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
                'DurationField': 'bigint',
                'FileField': 'varchar(%(max_length)s)',
                'FilePathField': 'varchar(%(max_length)s)',
                'FloatField': 'double precision',
                'IntegerField': 'integer',
                'BigIntegerField': 'bigint',
                'IPAddressField': 'char(15)',
                'GenericIPAddressField': 'char(39)',
                'NullBooleanField': 'bool',
                'OneToOneField': 'integer',
                'PositiveIntegerField': 'integer UNSIGNED',
                'PositiveSmallIntegerField': 'smallint UNSIGNED',
                'SlugField': 'varchar(%(max_length)s)',
                'SmallIntegerField': 'smallint',
                'TextField': 'longtext',
                'TimeField': 'time',
                'UUIDField': 'char(32)',
    
        BooleanField(Field)
            - 布尔值类型
    
        NullBooleanField(Field):
            - 可以为空的布尔值
    
        CharField(Field)
            - 字符类型
            - 必须提供max_length参数, max_length表示字符长度
    
        TextField(Field)
            - 文本类型
    
        EmailField(CharField):
            - 字符串类型,Django Admin以及ModelForm中提供验证机制
    
    
        GenericIPAddressField(Field)
            - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
            - 参数:
                protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
                unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
    
        URLField(CharField)
            - 字符串类型,Django Admin以及ModelForm中提供验证 URL
    
        SlugField(CharField)
            - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
    
        CommaSeparatedIntegerField(CharField)
            - 字符串类型,格式必须为逗号分割的数字
    
        UUIDField(Field)
            - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
    
        FilePathField(Field)
            - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
            - 参数:
                    path,                      文件夹路径
                    match=None,                正则匹配
                    recursive=False,           递归下面的文件夹
                    allow_files=True,          允许文件
                    allow_folders=False,       允许文件夹
    
        FileField(Field)
            - 字符串,路径保存在数据库,文件上传到指定目录
            - 参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
    
        ImageField(FileField)
            - 字符串,路径保存在数据库,文件上传到指定目录
            - 参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
                width_field=None,   上传图片的高度保存的数据库字段名(字符串)
                height_field=None   上传图片的宽度保存的数据库字段名(字符串)
    
        DateTimeField(DateField)
            - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
    
        DateField(DateTimeCheckMixin, Field)
            - 日期格式      YYYY-MM-DD
    
        TimeField(DateTimeCheckMixin, Field)
            - 时间格式      HH:MM[:ss[.uuuuuu]]
    
        DurationField(Field)
            - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
    
        FloatField(Field)
            - 浮点型
    
        DecimalField(Field)
            - 10进制小数
            - 参数:
                max_digits,小数总长度
                decimal_places,小数位长度
    
        BinaryField(Field)
            - 二进制类型
    View Code

    主键拿出来说下

    自增id,之前定义表结构的时候,省略了主键,让Django帮我创建了自增id。也可以自己定义主键和自增id:

    id = models.AutoField(primary_key=True),一定要加后面的     primary_key=True
    class User(models.Model):
        id = models.AutoField(primary_key=True)  # 数据类型是自增,并且设为主键
        g = models.CharField(max_length=32)

    6.字段的参数

    null                数据库中字段是否可以为空
        db_column           数据库中字段的列名
        db_tablespace
        default             数据库中字段的默认值
        primary_key         数据库中字段是否为主键
        db_index            数据库中字段是否可以建立索引
        unique              数据库中字段是否可以建立唯一索引
        unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
        unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
        unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
    
        verbose_name        Admin中显示的字段名称
        blank               Admin中是否允许用户输入为空
        editable            Admin中是否可以编辑
        help_text           Admin中该字段的提示信息
        choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                            如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
    
        error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                            字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                            如:{'null': "不能为空.", 'invalid': '格式错误'}
    
        validators          自定义错误验证(列表类型),从而定制想要的验证规则
                            from django.core.validators import RegexValidator
                            from django.core.validators import EmailValidator,URLValidator,DecimalValidator,
                            MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                            如:
                                test = models.CharField(
                                    max_length=32,
                                    error_messages={
                                        'c1': '优先错信息1',
                                        'c2': '优先错信息2',
                                        'c3': '优先错信息3',
                                    },
                                    validators=[
                                        RegexValidator(regex='root_d+', message='错误了', code='c1'),
                                        RegexValidator(regex='root_112233d+', message='又错误了', code='c2'),
                                        EmailValidator(message='又错误了', code='c3'), ]
                                )
    
    参数
    View Code

    db_column :数据库中字段的列名。默认列明就是我们的变量名,可以通过这个参数设置成不一样的  

    class UserInfo(models.Model):
        username = models.CharField(max_length=32)  # 列名就是变量名 username
        password = models.CharField(max_length=64, db_column='pwd')  # 数据库中的列名是 pwd

    auto_now :自动生成一个当前时间,数据更新时(包括创建),注意更新数据的写法,和前面的不同
    auto_now_add :自动生成一个当前时间,数据创建时

    class UserInfo(models.Model):
        # 用户注册时会生成用户名和密码
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        # 创建记录时会生成当前时间存放在ctime里,这个就是用户的注册时间
        ctime = models.DateTimeField(auto_now_add=True)
        # 用户修改密码会更新uptime的时间,这个就是上次修改密码的时间
        uptime = models.DateTimeField(auto_now=True)

    #创建数据
    # models.UserInfo.objects.filter(id=nid).update(password='1234') 这种方法更新是不会刷新 auto_now 的时间的
    # 用save()方法更新可以刷新 auto_now 的时间
    # obj =  models.UserInfo.objects.filter(id=nid).first()
    # obj.password = '4321'
    # obj.save()

    choices :Admin中显示选择框的内容。(用不变动的数据放在内存中从而避免连表操作,连表操作会涉及到性能问题)

    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
        # 用户有各种类型
        user_type_choices = (
            (1, '管理员')
            (2, '普通用户')
            (3, '访客')
        )
        # 定义一个用户类型的字段
        user_type_id = models.IntegerField(choices=user_type_choices, default=2)
    # 这样数据库里是一个整数类型,值是1、2、3。使用字段名取值 obj.user_type_id 获取到的是数值
    # 如果要获取后面的内容,使用 get_FOO_display() 方法, 即 obj.get_user_type_id_display()
    # 但是我们在admin里看选择框的时候看到的是“管理员”、“普通用户”、“访客”,这就是因为把选项所对应的内容放到了内存中了
    # 有了Django这个功能就不用再搞一张表,存放各个数值对应的内容了,还要做外键关联,用的时候还要连表查询
    # 即使不用admin,我们也可以在自己的代码里读取这个属性获取到内容,避免连表查询

    error_messages :自定义错误信息(字典类型)。字典key:null、blank、invalid、invalid_choice、unique、unique_for_date

    class UserInfo(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64, error_messages={'null': "不能为空", 'invalid': '格式错误'})

    help_text :Admin中该字段的提示信息。默认没有提示信息,设置后会显示在input框的下方

     外键操作

    一对多

    创建外键关联-修改表结构

    usergroup = models.ForeignKey(User_Group,on_delete=models.CASCADE,default=1)
    ForeignKey创建外键

    第一次创建的时候,我没有添加on_delete=models.CASCADE

    在使用python manage.py makeigrations 进行迁移的时候的出错了,报错如下:

    经过筛查,在创建多对一的关系的,需要在Foreign的第二参数中加入on_delete=models.CASCADE  主外关系键中,级联删除,也就是当删除主表的数据时候从表中的数据也随着一起删除

    on_delete=models.CASCADE 
    default=1  设置外键默认值

     外键关系创建完后,在user表里面会生成一列usergroup_id,并且生成了一个usergroup类

    此时要查询usergroup表中的内容,在html中可以使用 {{row.usergroup.dept}},row为views传过来的user表中所有对象

    通过对象获取部门

  • 相关阅读:
     Go is more about software engineering than programming language research.
    perl 处理json 数组格式
    perl 处理json 数组格式
    mongodb 限制ip访问
    mongodb 限制ip访问
    haproxy 访问www.zjdev.com 自动跳转到appserver_8001 对应的nginx
    haproxy 访问www.zjdev.com 自动跳转到appserver_8001 对应的nginx
    docker 数据盘映射方案
    docker 数据盘映射方案
    perl encode_utf8 和decode_utf8
  • 原文地址:https://www.cnblogs.com/Aline2/p/10081674.html
Copyright © 2020-2023  润新知