Django初识

Django的目录结构

命令创建了一个mysite的工程 django-admin.py startproject mysite

然后在创建好的mysite目录下使用命令创建一个blog应用python manage.py startapp blog

创建好的目录如下所示

需要注意的是 templatetags是自定义的标签,保存自己创建的.py的文件

Django MTV模式

Django即WEB框架的一种,即使用别人搭建好的东西来简化自己的操作。

Django的MTV模式本质上与MVC模式没有什么差别,也是各组件之间为了保持松耦合关系,只是定义上有些许不同,Django的MTV分别代表:

  • Model(模型):负责业务对象与数据库的对象(ORM)
  • Template(模版):负责如何把页面展示给用户
  • View(视图):负责业务逻辑,并在适当的时候调用Model和Template

 此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template

Django流程命令执行工具

django
    #安装: pip3 install django
          添加环境变量
    #1  创建project
       django-admin startproject mysite

    #2  创建APP       
       python mannage.py startapp  app01

    #3  settings配置 
       TEMPLATES

       STATICFILES_DIRS=(
            os.path.join(BASE_DIR,"statics"),
        )

       STATIC_URL = '/static/' 
       #  STATICFILES_DIRS是必须要有的,无论 STATIC_URL有没有设定 
      
      <script src="/statics/jquery-3.1.1.js"></script>
      <script src="/static/jquery-3.1.1.js"></script>    
      
      采用第二种方式 ,后端的更改不会影响前端的引入,避免造成前端大量修改

    #4  根据需求设计代码
           url.py
           view.py
    #5  使用模版
       render(req,"index.html")   
    #6  启动项目
       python manage.py runserver  127.0.0.1:8090
    #7  连接数据库,操作数据
       model.py

常用的命令行使用

创建一个django工程 : django-admin.py startproject mysite

在mysite目录下创建blog应用: python manage.py startapp blog
#blog是自己启动名字

启动django项目:python manage.py runserver 127.0.0.1:8080
#不写IP默认是127.0.0.1

生成同步数据库的脚本:python manage.py makemigrations  
            同步数据库:   python manage.py migrate   

清空数据库: python manage.py  flush

查询某个命令的详细信息: django-admin.py  help  startapp

启动交互界面 :python manage.py  shell

终端上输入 python manage.py
# 可以看到详细的列表,在忘记子名称的时候特别有用。

Django URL(路由系统)

简洁、优雅的URL模式在高质量的WEB应用中是一个非常重要的细节。设计URL之前,需要创建一个python模块,通常称为URLconf(URLconfiguration)。这个模块是纯粹的python代码,包含URL模式(简单正则)到python函数(视图)的简单映射。基本格式如下

urlpatterns = [
 
    url(正则表达式, views视图函数,参数,别名),
     
]
  • 一个正则表达式字符串
  • 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 可选的要传递给视图函数的默认参数(字典形式)
  • 一个可选的name参数

需要注意的是在一个很大的网站下,我们不可能将所有的URL都放到一个目录下,这时我们需要用到解耦去完成,将同一模块下的URl放在一个目录下进行操作。

如下:需要用到 include

from django.conf.urls import include, url

urlpatterns = [
   url(r'^admin/', admin.site.urls),
   url(r'^blog/', include('blog.urls')),
]
#输入URL会通过总的去blog下寻找URL完成操作

对于额外的参数给视图函数。

django.conf.urls.url() 函数可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。

例如

from django.conf.urls import url
from blog import views
urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

在这个例子中,对于 /blog/2005/ 请求,Django 将调用 view.year_archive(request,year='2005',foo='bar')

Django view(视图函数)

一个视图函数,或者简短来说叫做视图,是一个简单的python函数,它接受web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张照片等。

http请求中产生两个核心对象:

  • http请求:HttpRequest对象
  • http响应:HttpResponse对象
# path:       请求页面的全路径,不包括域名
#
# method:     请求中使用的HTTP方法的字符串表示。全大写表示。例如
#
#                    if  req.method=="GET":
#
#                              do_something()
#
#                    elseif req.method=="POST":
#
#                              do_something_else()
#
# GET:         包含所有HTTP GET参数的类字典对象
#
# POST:       包含所有HTTP POST参数的类字典对象
#
#              服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过
#              HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用
#              if req.POST来判断是否使用了HTTP POST 方法;应该使用  if req.method=="POST"
#
#
#
# COOKIES:     包含所有cookies的标准Python字典对象;keys和values都是字符串。
#
# FILES:      包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中                     name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
#
#             filename:      上传文件名,用字符串表示
#             content_type:   上传文件的Content Type
#             content:       上传文件的原始内容
#
#
# user:       是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前
#              没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你
#              可以通过user的is_authenticated()方法来辨别用户是否登陆:
#              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
#              时该属性才可用
#
# session:    唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
HttpRequest对象属性

相对于HttpRequest对象来说,HttpResponse是由自己创建,每个VIEW请求必须返回一个HttpResponse对象。

扩展的几种对象有

页面渲染:render(request,"xx.html"),
              render_to_response("xx.html"),
页面跳转:redirect()
 
locals():   可以直接将函数中所有的变量传给模板
name=xx
render(request,"xx.html",locals())#这时会把name这个变量传递过去,有几个传递几个                        

Template 

模板基本语法

>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.

逻辑控制代码块的组成  

  • 变量:使用双大括号引用变量{{var_name}}
  • 标签tag:大括号和百分号的组合 {% load staticfiles %}
  • 过滤器filter的使用:{{ship_date|date:"Fj,Y"}},ship_date变量传给data过滤器,date过滤器通过使用"FJ,Y"这几个参数来格式化日期数据。"|"代表类似Unix命令中的管道操作。

常用标签

 {%if%} 的使用  {% endif %}表示结束

      可以使用你的and,or,not来组织的逻辑。但不允许and和or同时出现在条件语句中。新版本中已经支持了{%elif%} 这样的用法。

{% for %}的使用  {% endfor %}表示结束

      用来循环一个序列, 还可以使用reserser关键字来进行倒序遍历,一般可以先用if语句判断序列是否为空,再进行遍历;还可以使用empty关键字来进行为空时的跳转

{%csrf_token%}

 用于生成csrf_token的标签,用于防治跨站攻击验证。只有在form表单提交时才会有效

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>

如果是render_to_response时还需设置参数,所以最好用render

{% load %}: 加载标签库 

{% url %}:  引用路由配置的地址

 {% with %}:用更简单的变量名替代复杂的变量名 

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}

 {% verbatim %}: 禁止render   

{% verbatim %}
         {{ hello }}
{% endverbatim %}

常用过滤器  

  • add          :   给变量加上相应的值
  • addslashes:    给变量中的引号前加上斜线
  • capfirst     :    首字母大写
  • cut           :   从字符串中移除指定的字符
  • date         :   格式化日期字符串
  • default     :   如果值是False,就替换成设置的默认值,否则就是用本来的值
  • default_if_none:如果值是None,就替换成设置的默认值,否则就使用本来的值

举几个例子来看看如何去使用

{#value1="aBcDe" 定义一个变量        #}
{{ value1|upper }} #结果为 ABCDE

{#    value2=5#}
{{ value2|add:3 }} #结果为8

{#    value6='<a href="#">跳转</a>'#}

{{ value6 }} #结果<a href="#">跳转</a>

{% autoescape off %}
  {{ value6 }}              
{% endautoescape %}#结果为HTML修饰的的跳转
{{ value6|safe }}#结果为HTML修饰的的跳转
#所以说这两种是告诉浏览器这两者是安全的,可以死去做渲染返回给用户

自定义标签  

先创建templatetags模块,这样Django才能找到

再在templatetags下创建任意的py文件,这里我创建的是add100.py,里面内容如下

from django import template

register = template.Library() #固定的格式必须这样写
@register.filter
def my_add100(v1, v2):  #最多接收两个参数,但能添加到if 条件语句下
    return v1+100+v2

@register.simple_tag   #不能添加到if 条件语句下
def my_add(v1, v2, v3):
    return v1+v2+v3

在通过URL连接的py文件去找到我们自己的创建的add100去操作

{% load add100 %} 必须写


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>

<body>
{#filter#}
{{ num|my_add100:8  }}

{#simple_tag#}
{% my_add num 10 10 %}

{% if num|my_add100:3 %}
    <p>{{ num|my_add100:3 }}</p>
    
{% endif %}
</body>
</html>

extend(继承) 

主要是在WEB开发中,会存在很多重复的代码,这就会在成代码冗余。所以就用到了继承去除不同部分代码来分开写,这就避免了这一问题。之前传统的方法是使用 {% include %} 方法,经过改进,现在有了更好的方法。先举个简单的例子

视图中的函数。

def ordered(req):

    return render(req,"ordered.html")

def shopping_car(req):

    return render(req,"shopping_car.html")

共同HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .page-header{
            height: 50px;
            background-color: rebeccapurple;
        }

        .page-body .menu{
            height: 500px;
            background-color: antiquewhite;
            float: left;
            width: 20%;
        }

        .page-body .content{
            height: 500px;
            background-color: cornflowerblue;
            float: left;
            width: 80%;
        }
        .page-footer{
            height: 50px;
            background-color: darkcyan;
            clear: both;
        }
    </style>
</head>
<body>
<div>
    <div class="page-header"></div>
    <div class="page-body">
        <div class="menu">
            <a href="/ordered/">订单</a><br>
            <a href="/shopping_car/">购物车</a>
        </div>




        <div class="content">

            {% block content %}
                <div class="xxx" style="color: red">pppppp</div>
            {% endblock %}

        </div>
    </div>
    <div class="page-footer"></div>


</div>
</body>
</html>
base

不同部分HTML

{% extends "base.html" %}


{% block content %}

       {{ block.super }}
       <div>订单</div>

{% endblock %}
ordered
{% extends "base.html" %}


{% block content %}
        <div class="content">
            购物车2
        </div>
{% endblock %}
shopping_car

使用继承模板的时候需要注意的地方

  • 如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
  • 一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
  • 如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
  • 如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
  • 不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。

( . )访问变量属性   

从技术上讲,当模板遇到点,它将以这样的顺序查找

  • 字典查询
  • 属性或方法查询
  • 数字索引查询

如果计算结果的值是可调用的,它将被无参数的调用,调用结果将成为模板的值。

>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
u'Sally is 43 years old.'

也可用来引用对象的方法,例如

>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
u'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
u'123 -- 123 -- True'

Models

模型是你数据唯一、权威的数据源。它包含你所存储数据的必要字段和行为。通常,每个模型对应数据库中的唯一一张表。

  • 每个模型都是django.db.models.Model的一个python子类。
  • 模型中的每个属性都表示为数据库的一个字段。
  • Django提供一套自动生成的,用于数据库访问的API。

在Django中默认使用splite数据库,在settings配置如下

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

如果想要引用其它数据库,可修改settings配置如下

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'django_com',
        'USER':'root',
        'PASSWORD':'',

    }
}

注意如果启动数据库时报错,是因为导入的驱动有问题,需要我们重新导入,需要找到项目文件下的__init__文件写入即可解决问题。

import pymysql
pymysql.install_as_MySQLdb()

ORM(对象关系映射)     

用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。

优点:

  • ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
  • 可以避免一些新手程序猿写sql语句带来的性能问题。

缺点:

  • 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
  • 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
  • 通过QuerySet的query属性查询对应操作的sql语句

创建表结构  

from django.db import models

# Create your models here.


class Publish(models.Model):
    name = models.CharField(max_length=64)
    city = models.CharField(max_length=64)

    def __str__(self):
        return self.city

class Book(models.Model):

    title = models.CharField(max_length=64)
    price = models.IntegerField()
    pagenum = models.IntegerField(null=True)
    publisher = models.ForeignKey(Publish)  # 关联表的外键,一对多


    #接受对象
    author = models.ManyToManyField("Author") # 多对多的联系

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=64)
    def __str__(self):
        return self.name
表结构
from django.db import models

# Create your models here.


class Publish(models.Model):
    name = models.CharField(max_length=64)
    city = models.CharField(max_length=64)

    def __str__(self):
        return self.city

class Book(models.Model):

    title = models.CharField(max_length=64)
    price = models.IntegerField()
    pagenum = models.IntegerField(null=True)
    publisher = models.ForeignKey(Publish)  # 关联表的外键,一对多

    # 接受对象
    author = models.ManyToManyField("Author") # 多对多的联系

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=64)
    def __str__(self):
        return self.name
多表关联操作

默认情况下,在多对多表操作时,第三张表是Django内部帮我们创建的,当然我们也可以自己创建。

class Book2Author(models.Model):
            author=models.ForeignKey("Author")
            book=  models.ForeignKey("Book")
            class Meta:
                    unique_together=["author","book"]
第三张表结构

创建好后,记得同步数据库

生成同步数据库的脚本:python manage.py makemigrations  
            同步数据库:   python manage.py migrate  
# AutoField
# 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.(参阅 _自动主键字段)
# BooleanField
# A true/false field. admin 用 checkbox 来表示此类字段.
# CharField
# 字符串字段, 用于较短的字符串.
# 
# 如果要保存大量文本, 使用 TextField.
# 
# admin 用一个 <input type="text"> 来表示此类字段 (单行输入).
# 
# CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.
# 
# CommaSeparatedIntegerField
# 用于存放逗号分隔的整数值. 类似 CharField, 必须要有 maxlength 参数.
# DateField
# 一个日期字段. 共有下列额外的可选参数:
# 
# Argument    描述
# auto_now    当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
# auto_now_add    当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
# admin 用一个文本框 <input type="text"> 来表示该字段数据(附带一个 JavaScript 日历和一个"Today"快键.
# 
# DateTimeField
#  一个日期时间字段. 类似 DateField 支持同样的附加选项.
# admin 用两上文本框 <input type="text"> 表示该字段顺序(附带JavaScript shortcuts). 
# 
# EmailField
# 一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数.
# FileField
# 一个文件上传字段.
# 
# 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don't fill up the given directory).
# 
# admin 用一个``<input type="file">``部件表示该字段保存的数据(一个文件上传部件) .
# 
# 在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
# 
# 在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. (出于性能考虑,这些文件并不保存到数据库.) 定义 MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 WEB 服务器用户帐号是可写的.
# 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件.
# 你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 出于习惯你一定很想使用 Django 提供的 get_<fieldname>_url 函数.举例来说,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 这样的方式得到图像的绝对路径.
# FilePathField
# 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
# 
# 参数    描述
# path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. Example: "/home/images".
# match    可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. 注意这个正则表达式只会应用到 base filename 而不是路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
# recursive    可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
# 这三个参数可以同时使用.
# 
# 我已经告诉过你 match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
# 
# FilePathField(path="/home/images", match="foo.*", recursive=True)
# ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
# 
# FloatField
# 一个浮点数. 必须 提供两个 参数:
# 
# 参数    描述
# max_digits    总位数(不包括小数点和符号)
# decimal_places    小数位数
# 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
# 
# models.FloatField(..., max_digits=5, decimal_places=2)
# 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
# 
# models.FloatField(..., max_digits=19, decimal_places=10)
# admin 用一个文本框(<input type="text">)表示该字段保存的数据.
# 
# ImageField
# 类似 FileField, 不过要校验上传对象是否是一个合法图片.它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存.
# 
# 该字段要求 Python Imaging Library.
# 
# IntegerField
# 用于保存一个整数.
# 
# admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框)
# 
# IPAddressField
# 一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
# 
# admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框)
# 
# NullBooleanField
# 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项.
# 
# admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.
# 
# PhoneNumberField
# 一个带有合法美国风格电话号码校验的 CharField``(格式: ``XXX-XXX-XXXX).
# PositiveIntegerField
# 类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的....所以字段名字取得不太好,无符号整数就对了嘛).
# PositiveSmallIntegerField
# 类似 PositiveIntegerField, 取值范围较小(数据库相关)
# SlugField
# "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs.
# 
# 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. 在以前的 Django 版本,没有任何办法改变 50 这个长度.
# 
# 这暗示了 db_index=True.
# 
# 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form:
# 
# models.SlugField(prepopulate_from=("pre_name", "name"))
# prepopulate_from 不接受 DateTimeFields.
# 
# admin 用一个``<input type="text">``表示 SlugField 字段数据(一个单行编辑框) 
# 
# SmallIntegerField
# 类似 IntegerField, 不过只允许某个取值范围内的整数.(依赖数据库)
#  
# TextField
# 一个容量很大的文本字段.
# 
# admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).
# 
# TimeField
# A time. Accepts the same auto-population options as DateField 和 DateTimeField.
# 
# admin 用一个 <input type="text"> 文本框表示该字段保存的数据(附加一些JavaScript shortcuts).
# 
# URLField
# 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应).
# 
# admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)
# 
# USStateField
# 一个两字母的美国州名缩写.
# 
# admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)
# 
# XMLField
# 一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema 的文件系统路径.
常用字段

添加数据(create、save)

def login(req):

    # create方法1
    # dic= {"title":"python","price":35}
    # Book.objects.create(**dic)
    # return HttpResponse("OK")

    # create方法2
    # Book.objects.create(
    #     title="python1",
    #     price=45
    # )

    # save 方法 1
    # obj = Book(title="python2",price=55)
    # obj.save()

    # save 方法 2
    # obj = Book()
    # obj.title = "python3"
    # obj.price = 65
    # obj.save()
    # return HttpResponse("ok")
create、save

向一对多或多对多关联表插入数据时需要注意的事项是:

pub = models.Publish.objects.filter(id=1)
    Book.objects.create(
        title="JAVA2",
        price=55,
        # 第一种,因为外键关系是一对多,publisher只能对应一个对象,所以值只能是一个对象,不能是一个集合
        # publisher=pub[0],

        # 第二种直接根据ID匹配,也不用 pub = models.Publish.objects.filter(id=1)
        publisher_id=2
    )
一对多
 book = models.Book.objects.filter(id=6)[0]
    # authors = models.Author.objects.filter(id=1)[0] # 获取的是一个几个
    authors = models.Author.objects.get(id=3)  # get获取的是一个对象不是一个集合
    book.author.add(authors) # 正向查询

    # authors.book_set(book) # 反向查询
多对多

修改数据(update,save)  

 两种增加数据的实例

author = models.Author.get(id=2).name = 'flash'
author.save()

models.Publish.objects.filter(id=2).update(name="aaa")

这里你需要注意的是 update是QuerySet 对象的方法,所以不能更新返回是model对象的结果。相比于save方法,update放大效率更高。

因为,save去更新数据时,会将要更新的那列数据全部更细腻一遍,即使没有赋予新的值,也会将原来的值,重新去赋值一遍,这就是导致save效率低的原因。而update方法只会更新你要更新的那个列。

查找数据     

常见查询

# 查询相关API:

#  <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象

#  <2>all():                 查询所有结果

#  <3>get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

#-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()--------

#  <4>values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
                                     
#  <5>exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象

#  <6>order_by(*field):      对查询结果排序

#  <7>reverse():             对查询结果反向排序

#  <8>distinct():            从返回结果中剔除重复纪录

#  <9>values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

#  <10>count():              返回数据库中匹配查询(QuerySet)的对象数量。

# <11>first():               返回第一条记录

# <12>last():                返回最后一条记录

#  <13>exists():             如果QuerySet包含数据,就返回True,否则返回False。
API

单表查询和多表查询

#正向查找---属性
ret = models.Book.objects.filter(id=2)[0]
print(ret.title)
print(ret.publisher.name)

#反向查找----属性
ret2=models.Publish.objects.last()
print(ret2.name)
print(ret2.city)
print(ret2.book_set.all()) #ret2.book_set是一个queryset集合


#可以利用双下线去做条件判断
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in不在的显示出来
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
单表查询
#一对多
et4=models.Book.objects.filter(title='Python').values('publisher__city')
print(ret4)  #[{'publisher__city': '北京'}]
ret5=models.Book.objects.filter(publisher__address='北京').values('publisher__name')
print(ret5)   #[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}]

 #正向查找(条件)之多对多
      ret6=models.Book.objects.filter(title='Python').values('author__name')
 print(ret6)
      ret7=models.Book.objects.filter(author__name="alex").values('title')
print(ret7)


#反向查找之一对多:
    ret8=models.Publisher.objects.filter(book__title='Python').values('name')
print(ret8)#[{'name': '人大出版社'}]  注意,book__title中的book就是Publisher的关联表名

#反向查找之多对多:
    ret10=models.Author.objects.filter(book__title='Python').values('name')
print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}] 
多表查询

多对多表的补充   

class Book(models.Model):
    name =models.CharField(max_length=32)

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    m = models.ManyToManyField('Book')

    def __str__(self):
        return self.name
表结构
def test(request):

    author_list = models.Author.objects.values('id','name','m','m__name')
    for item in author_list:
        print(item['id'], item['name'], '书籍ID:', item['m'], item['m__name'])


    # 正向查找
    obj = models.Author.objects.get(id=1)
    # 增加
    # obj.m.add(2)
    # obj.m.add(2,3)
    # obj.m.add(*[1,3])

    #删除
    # obj.m.remove(2)
    # obj.m.remove(2,3)
    # obj.m.remove(*[1,3])
    #
    # 清空
    # obj.m.clear()

    # 更新
    # obj.m.set([1,2,3])


    # 反向查找
    # obj = models.Book.objects.get(id=1)
    # obj.author_set.add(1)

    return HttpResponse('ok')
View Code

 

聚合的两种方式

  • 在查询上生成聚合 aggregate
  • 为查询集的每一项生成聚合 annotate

1、比如我们想要计算所有图书的平均价钱。Django的查询语法提供aggregate的方法。首先我们找到所有图书

#使用之前先引入
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))

aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值。

aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。

2、annotate为QuerySet中每一个对象都生成一个独立的汇总值。比如如果检索一列图书,你可能想知道有多少作者写了一本书。

from django.db.models import Count
q = Book.objects.annotate(Count('authors'))

q[0]
<Book: The Definitive Guide to Django>
 q[0].authors__count
2

q[1]
<Book: Practical Django Projects>
 q[1].authors__count
1  

与aggregate()不同的是,annotate()不是一个终止子句,annotate()子句返回结果是一个查询集(QuerySet)。这个查询集可以用到任何QuerySet的方法进行操作。

 

在查询一些复杂的关系时,比如求与、或等时,我们需要用到 F/Q查询。  

  • F 查询专门取对象中某列的值得操作
  • Q 是构造搜索条件去查找
from django.db.models import F
models.Tb1.objects.update(num=F('num')+1) 
#得到结果是将TB1表中的每个num的数值加 1
F 实例
from django.db.models import Q

#1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
q1=models.Book.objects.filter(Q(title__startswith='P')).all()
print(q1)#[<Book: Python>, <Book: Perl>]

# 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
Q(title__startswith='P') | Q(title__startswith='J')

# 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
Q(title__startswith='P') | ~Q(pub_date__year=2005)

# 4、应用范围:

Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

#sql 语句对比:
# SELECT * from polls WHERE question LIKE 'P%'
#     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')


# 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
# 正确用法:
Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), title__startswith='P')
Q 实例
extra
    #
    # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

    # F
    #
    # from django.db.models import F
    # models.Tb1.objects.update(num=F('num')+1)


    # Q
    #
    # 方式一:
    # Q(nid__gt=10)
    # Q(nid=8) | Q(nid__gt=10)
    # Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
    # 方式二:
    # con = Q()
    # q1 = Q()
    # q1.connector = 'OR'
    # q1.children.append(('id', 1))
    # q1.children.append(('id', 10))
    # q1.children.append(('id', 9))
    # q2 = Q()
    # q2.connector = 'OR'
    # q2.children.append(('c1', 1))
    # q2.children.append(('c1', 10))
    # q2.children.append(('c1', 9))
    # con.add(q1, 'AND')
    # con.add(q2, 'AND')
    #
    # models.Tb1.objects.filter(con)


    # 执行原生SQL
    #
    # from django.db import connection, connections
    # cursor = connection.cursor()  # cursor = connections['default'].cursor()
    # cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    # row = cursor.fetchone()
F&Q系统运用

admin配置

admin是Django自带的一个后台管理app

我们在启动Django服务后直接访问admin可以得到一个登陆界面如下。默认是英文,修改语言需要进到settings下去修改即可

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'  

默认没有用户,我们首先注册一个管理员用户 python manage.py createsuperuser 即可

进入后我们先将数据库引进

  • 使用register的方法
from django.contrib import admin

# Register your models here.
from app.models import *
class PubAdmin(admin.ModelAdmin):
    pass
admin.site.register(Author,PubAdmin)  # 直接使用register方法

我们可以根据自己的需求去设置页面展示的效果

  • list_display:  指定要显示的字段
  • search_fields:指定搜索的字段
  • list_filter: 指定列表过滤器
  • ordering:指定排序字段
class PubAdmin(admin.ModelAdmin):
    list_display = ("name", "city") # name,city为注册数据库内的属性
    search_fields = ("name",) # 相当于指定一个搜索框
    list_filter = ("name",)
    ordering = ("name",)

 

 

官方文档 https://docs.djangoproject.com/en/1.10/ref/contrib/admin/  

  

 

  

  

 

 

  

  

  

posted @ 2016-12-01 17:22  Dwyane.wang  阅读(241)  评论(0编辑  收藏  举报