大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

第18天,Django进阶

@(Django)[笔记]

django 项目手动删除数据库表后重新同步出错的问题解决办法
http://blog.csdn.net/runner__1/article/details/74171979

python manage.py sqlmigrate your_app_name 0001

your_app_name换成自己的app名字,0001是you_app_name/migrations目录下的记录,执行即可看到框架自动生成的创建表sql语句

目录

一、模板(Templates)进阶
    1.1 模板继承
    1.2 自定义filter 和simple_tag
二、ORM 进阶
    2.1 将数据库引擎改为MySQL
    2.2 创建表
        2.2.1建立一对一的关系
        2.2.2 建立一对多和多对多的关系
        2.2.3 执行命令
    2.3 添加表记录
        2.3.1 普通字段插入数据
        2.3.2 外键字段插入数据
        2.3.3 多对多字段插入数据
        2.3.4 解除多对多关系
        2.3.5 通过web界面添加表记录
    2.4 查询表记录
        all()
        filter()
        exclude()
        values()
        values_list()
        order_by()
        reverse()
        distinct()
        count()
        exits()
        get()
        first()
        last()
    2.5 双下线之单表查询

http://www.cnblogs.com/yuanchenqi/articles/7552333.html

一、模板(Templates)进阶

1.1 模板继承

网站模板的设计,一般的,我们做网站有一些通用的部分,比如 导航,底部,访问统计代码等等
nav.html, bottom.html, tongji.html

可以写一个 base.html 来包含这些通用文件(include)

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}默认标题{% endblock %} - 自强学堂</title>
</head>
<body>
 
{% include 'nav.html' %}
 
{% block content %}
<div>这里是默认内容,所有继承自这个模板的,如果不覆盖就显示这里的默认内容。</div>
{% endblock %}
 
{% include 'bottom.html' %}
 
{% include 'tongji.html' %}
 
</body>
</html>

注意:block中的title、content都是自定义的名字,也就是为{% block xxx %} {% endblock %}包含的这一区域取一个名字,名字不能重复。

如果需要,写足够多的 block 以便继承的模板可以重写该部分,include 是包含其它文件的内容,就是把一些网页共用的部分拿出来,重复利用,改动的时候也方便一些,还可以把广告代码放在一个单独的html中,改动也方便一些,在用到的地方include进去。其它的页面继承自 base.html 就好了,继承后的模板也可以在 block 块中 include 其它的模板文件。

比如我们的首页 home.html,继承或者说扩展(extends)原来的 base.html,可以简单这样写,重写部分代码(默认值的那一部分不用改)

{% extends 'base.html' %}   <!-- 必须写在第一行 -->
 
{% block title %}欢迎光临首页{% endblock %}   <!-- 覆盖base.html模板中的title区域 -->
 
{% block content %}    <!-- 覆盖base.html模板中的content区域 -->
{% include 'ad.html' %}
这里是首页,欢迎光临
{% endblock %}

{{ block.super }}的用法

模板继承,在做block替换时,如果你不想将基板(base.html)中的内容覆盖,而是将基板(base.html)中的内容引用过来,再加上新内容,就需要用到{{ block.super }}变量;如下:

基板(base.html)内容如下:

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}默认标题{% endblock %} - 自强学堂</title>
</head>
<body>
{% block content %}
<div>在子模板中引用此部分内容</div>
{% endblock %}

</body>
</html>

子模板内容如下:

{% extends 'base.html' %}   <!-- 必须写在第一行 -->
 
{% block title %}欢迎光临首页{% endblock %}   <!-- 覆盖base.html模板中的title区域 -->
 
{% block content %}    
    {{ block.super }}    <!--会将基板中block content中的div标签引用到这里-->
    <p>这里是首页,欢迎光临</p>
{% endblock %}

最终展示到浏览的文件内容如下:

<!DOCTYPE html>
<html>
<head>
    <title>欢迎光临首页 - 自强学堂</title>
</head>
<body>

<div>在子模板中引用此部分内容</div>
<p>这里是首页,欢迎光临</p>

</body>
</html>

特别关注的点

注意:模板一般放在app下的templates中,Django会自动去这个文件夹中找。但 假如我们每个app的templates中都有一个 index.html,当我们在views.py中使用的时候,直接写一个 render(request, 'index.html'),Django 能不能找到当前 app 的 templates 文件夹中的 index.html 文件夹呢?(答案是不一定能,有可能找错)

Django 模板查找机制: Django 查找模板的过程是在每个 app 的 templates 文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个 app 的 templates 形成一个文件夹列表,Django 遍历这个列表,一个个文件夹进行查找,当在某一个文件夹找到的时候就停止,所有的都遍历完了还找不到指定的模板的时候就是 Template Not Found (过程类似于Python找包)。这样设计有利当然也有弊,有利是的地方是一个app可以用另一个app的模板文件,弊是有可能会找错了。所以我们使用的时候在 templates 中建立一个 app 同名的文件夹,这样就好了。

这就需要把每个app中的 templates 文件夹中再建一个 app 的名称,仅和该app相关的模板放在 app/templates/app/ 目录下面,

例如:项目 zqxt 有两个 app,分别为 tutorial 和 tryit

zqxt
├── tutorial
│   ├── __init__.py
│   ├── admin.py
│   ├── models.py
│   ├── templates
│   │   └── tutorial
│   │       ├── index.html
│   │       └── search.html
│   ├── tests.py
│   └── views.py
├── tryit
│   ├── __init__.py
│   ├── admin.py
│   ├── models.py
│   ├── templates
│   │   └── tryit
│   │       ├── index.html
│   │       └── poll.html
│   ├── tests.py
│   └── views.py
├── manage.py
└── zqxt
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

这样,使用的时候,模板就是 "tutorial/index.html" 和 "tryit/index.html" 这样有app作为名称的一部分,就不会混淆。

1.2 自定义filter 和simple_tag

  1. settings.py中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
  2. 在app中创建templatetags模块(模块名只能是templatetags),也就是在应用中创建一个名叫templatetags的python包;
  3. 创建任意.py文件,如:my_tag.py,内容如下:
from django import template
from django.utils.safestring import mark_safe

register = template.Library()   #register的名字是固定的,不可改变

@register.filter     #自定义过滤器必须加上这个装饰器
def filter_multi(v1,v2):
    return  v1 * v2
#过滤器只能接收两个参数,simple_tag可以接收多个参数

@register.simple_tag
def simple_tag_multi(v1,v2,v3):
    return  v1 * v2 * v3

@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)
    #mark_safe函数的功能和过滤器safe功能一样,都是把html标签当作标签渲染到页面上,而不是当作字符串
  1. 在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
{% load my_tag %}
  1. 在html模板中使用simple_tag和filter(如何调用)
{% load my_tag %}  
      
# num=12
{{ num|filter_multi:2 }} #24
 
{{ num|filter_multi:"[22,333,4444]" }}
 
{% simple_tag_multi 2 5 3 %}  #参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

注意:filter可以用在if等语句后,simple_tag不可以

{% if num|filter_multi:30 > 100 %}
   {{ num|filter_multi:30 }}
{% endif %}

二、ORM 进阶

2.1 将数据库引擎改为MySQL

  1. settings.py配置
 DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   
        'NAME': 's18day18',  #你的数据库名称   
        'USER': 'root',      #你的数据库用户名  
        'PASSWORD': '',      #你的数据库密码   
        'HOST': '',         #你的数据库主机,留空默认为localhost 
        'PORT': '3306',     #你的数据库端口
    }
}
  1. 数据库引擎更改
    假设应用的名称为app01,修改app01的__init__.py,即 app01/init.py
import pymysql
pymysql.install_as_MySQLdb()

如果想查看操作数据库对应的SQL语句,那么可以在settings.py中加入如下LOGGING

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            

'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
} 

接下来创建表

2.2 创建表

作者模型:一个作者有姓名和年龄。
作者详细信息模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

2.2.1建立一对一的关系

models.OneToOneField(to="关联表名")建立一对一的关系

from django.db import models

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()
 
    # 与AuthorDetail建立一对一的关系
    authorDetail=models.OneToOneField(to="AuthorDetail")
 
class AuthorDetail(models.Model):
 
    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

2.2.2 建立一对多和多对多的关系

建立一对多:models.ForeignKey(to="Publish",to_field="nid")

建立多对多:models.ManyToManyField(to='Author')

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()
 
 
class Book(models.Model):
 
    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    keepNum=models.IntegerField()
    commentNum=models.IntegerField()
 
    # 与Publish建立一对多的关系,外键字段建立在多的一方
    publish=models.ForeignKey(to="Publish",to_field="nid")
 
    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
    authors=models.ManyToManyField(to='Author')  

2.2.3 执行命令

python manage.py makemigrations
python manage.py migrate

2.3 添加表记录

2.3.1 普通字段插入数据

方式一:

publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com")
publish_obj.save() # 将数据保存到数据库

方式二:

publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com")

方式三:

publish_obj=Publish.objects.create(**request.POST.dict())

返回值puhlish_obj是添加记录的对象,也就是表中每个数据,将其封装成的一个对象。

2.3.2 外键字段插入数据

外键字段就是多对一字段

方式一:
基于对象的方式

publish_obj=Publish.objects.get(nid=1)
   Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish=publish_obj)

注意:是直接将对象赋值给Book类下的publish属性,book_obj.publish是一个出版商对象

方式二:
直接赋值,这个值必须是关联表已存在的

Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=665,pageNum=334,publish_id=1)

这是直接给数据库book表中的publish_id字段赋值

2.3.3 多对多字段插入数据

#先创建一个book_obj书籍对象
book_obj=Book.objects.create(title="追风筝的人",publishDate="2012-11-12",price=69,pageNum=314,publish_id=1)

#再创建两个作者对象
author_yuan=Author.objects.create(name="yuan",age=23,authorDetail_id=1)
author_egon=Author.objects.create(name="egon",age=32,authorDetail_id=2)

#将两个作者对象添加至authors里面,注意:authors是Book类下面的多对多定义的authors
book_obj.authors.add(author_egon,author_yuan)

# set()方法
#如果需要修改书籍的作者,比如将原来的author_egon和author_yuan换为author_alex、author_wu和author_oldboy,就需要用到**set()**方法了
book_obj.authors.set(author_alex,author_wu,author_oldboy)     #这样就可以直接覆盖原有的作者对象了。

注意: book_obj.authors是作者对象的集合,里面包含多个作者对象。all()set()方法还可以直接添加作者对象的id,也可以直接添加作者对象或者作者id的列表,假如作者id列表author_ids=[1,2,3],添加可以这样写:book_obj.authors.all(author_ids),修改可以这样写:book_obj.authors.set(author_ids)

2.3.4 解除多对多关系

book_obj.authors.remove()     
    # 将某个特定的对象从被关联对象集合中去除。还可以这样用book_obj.authors.remove(*[])
book_obj.authors.clear()       
    #清空被关联对象集合。

注意:对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法

2.3.5 通过web界面添加表记录

编辑admin.py

from . import models

admin.site.register(models.Author)
admin.site.register(models.Publish)
# python manage.py createsuperuser
输入账号和密码

# python manage.py runserver

浏览器访问
http://127.0.0.1:8000/admin

image.png

2.4 查询表记录

获取QuerySet对象的方法

QuerySet是盛放model对象的集合(类似一个列表),每个model对象就是一条表记录

all()

查询所有数据

res=Book.objects.all()
# res结果<QuerySet [<Book: Book object>, <Book: Book object>, ...]
# 得到的结果是一个个对象,不直观,可以在model中加入__str__方法,让它返回某一个字段,这里让它返回书名:
#例如:
class Book(models.Model):
    ...
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    ...
    def __str__(self):
        return self.title

#这时再查询all(),返回的结果就是书名了
#<QuerySet [<Book: 俊友>, <Book: 基督山伯爵>, ...]

filter()

查询符合过滤条件的记录

#查询书名是'俊友',并且价格为123的书
res=Book.objects.filter(title='俊友',price=123)
    #res结果<QuerySet [<Book: 俊友>]>

#查询价格小于100的书
res=Book.objects.filter(price__lt=100)
    #res结果<QuerySet [<Book: 呼啸山庄>, <Book: 威尼斯商人>,...]

exclude()

取反,筛选与条件不匹配的对象

#取价格不大于50的书
res=Book.objects.exclude(price__gt=50)
    #res结果<QuerySet [<Book: 呼啸山庄>, <Book: 围城>,...]

values()

括号里面放字段,要显示哪个字段就放在括号里,字段名称要用引号包含,返回的结果是QuerySet[]中包含着字典

#获取每本书的价格
res=Book.objects.values('price')
    #res结果<QuerySet [{'price': Decimal('123.00')}, {'price': Decimal('111.00')}, ...]

#过滤价格大于300的书,显示其价格
Book.objects.filter(price__gt=300).values('title','price')
    #<QuerySet [{'price': Decimal('999.00'), 'title': '五穆遗书'}]>

values_list()

它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列。

res=Book.objects.filter(price__gt=300).values_list('title','price')
    #结果<QuerySet [('五穆遗书', Decimal('999.00'))]>

order_by()

对查询结果排序,将查询字段放在括号中,用引号包括。默认是从小到大排序,要想从大到小排序,则需要在字段名前加一个负号“-”即可。

#正向按价格排序
res=Book.objects.filter(price__gt=280).order_by("price")

#返回排序
res=Book.objects.filter(price__gt=280).order_by("-price")

reverse()

对查询结果反向排序

res=Book.objects.filter(price__lt=30).reverse()

distinct()

去重,从返回结果中剔除重复纪录

#去除重复的publisher_id
res=Book.objects.values('publisher_id').distinct()
    #结果<QuerySet [{'publisher_id': 1}, {'publisher_id': 2}, {'publisher_id': 3}, ...]

count()

返回数据库中符合条件的对象数量

#查询价格大于250的书籍的数量
res=Book.objects.filter(price__gt=250).count()
    #res结果是23

exits()

查询如果有数据就返回True,否则就返回False

#判断是否有单价大于500的书
res=Book.objects.filter(price__gt=500).exists()
    #res结果为True

获取model对象的方法
也就是获取某一条表记录对象的方法

get()

返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

res=Book.objects.get(title='小妇人')

数据库中有多个书名叫小妇人,所以抛出如下异常

MultipleObjectsReturned at /query/
get() returned more than one Book -- it returned 2!

过滤的条件只能获取到一个对象时,就不会报错,如下:

res=Book.objects.get
    #res结果:碧雪剑

first()

返回QuerySet中第一条表记录对象

res=Book.objects.all().first()
    #res结果:俊友
image.png

last()

返回QuerySet中的最后一条表记录对象

res=Book.objects.all().last()
    #res结果:老友记
image.png

2.5 双下线之单表查询

Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人

Person.objects.filter(name__iexact="abc")  # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件



Person.objects.filter(name__contains="abc")  # 名称中包含 "abc"的人

Person.objects.filter(name__icontains="abc")  #名称中包含 "abc",且abc不区分大小写



Person.objects.filter(name__regex="^abc")  # 正则表达式查询

Person.objects.filter(name__iregex="^abc")  # 正则表达式不区分大小写


Person.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

Person.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
Person.objects.exclude(id__in=[11, 22, 33])  # not in

Person.objects.filter(id__range=[1, 2])      # 范围bettwen and

__startswith    #以什么字符开头
__istartswith   #以什么字符开头,忽略大小写
__endswith      #以什么字符结尾
__iendswith     #以什么字符结尾,忽略大小写